19 ms·
Modern C++ Won't Save Us (2019)
- pornel 6y agoUB-invoking dereference in std::optional is such a baffling design choice. The whole point of an optional type is to prevent accidental unchecked access to the value. Sure, sometimes it's useful for performance to skip the check when it's already known-safe from the context, but such dangerous optimization should have been behind a method like `beware_of_the_nasal_demons()`, not an innocent-looking convenience syntax for flirting with the UB — in a language that was supposed to be cleaning up the unsafety.
- dataflow 6y ago> The whole point of an optional type is to prevent accidental unchecked access to the value. You might argue it should be that, but it wasn't my impression that it is that. My impression was std::optional is just there to allow for representing the absence of a value. Ideally that should be zero-overhead, which means dereferencing it should degenerate into a normal object access. Hence the current design.
- nemothekid 6y ago>Ideally that should be zero-overhead Optionals are not zero-overhead in any other language. If you aren't going to bother checking the optional, why not just return any old pointer type? The type system should be used to enforce a contract and bypassing checks on that feels like you are just spitting in the fact of the type checker.
- dataflow 6y ago> Optionals are not zero-overhead in any other language. That's been true for so many other features in C++ too. > If you aren't going to bother checking the optional, why not just return any old pointer type? Pointers require something to point to. Optionals can embed the value. > The type system should be used to enforce a contract and bypassing checks on that feels like you are just spitting in the fact of the type checker. It's bypassing something alright, but I wouldn't say that's the type checker. And I mean, you could use this logic for everything else. Iterators, arrays, etc. should all have bounds checking too, right? If you want that, there's C#...
- lmm 6y ago> And I mean, you could use this logic for everything else. Iterators, arrays, etc. should all have bounds checking too, right? If you want that, there's C#... I mean yes, the logical conclusion is that trying to backport safety onto C++ is impossible and moving to another language is the only reasonable option. That's kind of the point of the article.
- dataflow 6y ago> I mean yes, the logical conclusion is that trying to backport safety onto C++ is impossible The logical conclusion is that what some people here seem to want is so radically different from C++ that they really just want an inherently just a different language altogether... which is fine. Nothing about this implies it's impossible to shoehorn C++ into something else (though that may still be true; I'm not sure). It just implies that another language should be considered when you want to prioritize memory safety, like maybe C# or Rust. (And nowhere here am I agreeing or disagreeing with the article.)
- nemothekid 6y ago>That's been true for so many other features in C++ too. Plenty of C++ features are zero-cost, not necessarily zero-overhead. To me having an optional without bounds checking is like obtaining a shared_ptr without incrementing it's use_count. It's like arguing that I should increment use_count manually so that copies are "zero-overhead". Sure it's "faster" but the design is still broken and will lead to all sorts of issues. >Pointers require something to point to. Optionals can embed the value. So? Just because it embeds a value rather than a pointer doesn't make your program any more correct. If the API designer returned an empty optional and you access it anyways you are still dealing with garbage data. >And I mean, you could use this logic for everything else. Iterators, arrays, etc. should all have bounds checking too, right? If you want that, there's C#... Ok? Then lets remove all overhead from the language. Why does shared_ptr increment it's refcount behind my back? No more shared_ptr. This kind of argument without nuance gets you nowhere. Likewise, unlike bounds checks, nothing about std::optional is forced or baked into the language. If you don't want to do null checking, then just don't use std::optional - the fast option is still there and unlike Rust, the fast option is the default. Optionals are a great tool to eliminate null pointers from a codebase. It's a big enough problem that I would expect an optional type, designed in 2018 to get it right. Allowing automatic dereferencing is an oversight.
- matvore 6y ago> why not just return any old pointer type? Using a pointer type in a straightforward manner would entail allocating the object on the heap. If allocated on the stack it has obvious lifetime limitations.
- emtel 6y ago> Optionals are not zero-overhead in any other language. This is a common misconception, and a pet peeve of mine. Properly designed optional types add neither space nor code-size overhead. Consider the C function: foo(int* ptr) { ... } If it is part of the API of foo that `ptr` may be null, then foo must not dereference `ptr` without verifying that it is non-null. So the body of foo must be something like: foo(int* ptr) { if (ptr != NULL) { ... } } Or, take the non-pointer case: foo(struct S s, bool s_present) { ... } Clearly the intent of this function is that the contents of `s` should not be accessed if `s_present` is false, so the implementation of the function must check `s_present` before inspecting `s`. (Inspecting s incorrectly wouldn't necessarily be UB, like in the previous example, but it's clearly erroneous) The only thing that a good optional type implementation does is make it a type error to fail to do what everyone agrees both of those functions must do anyway in order to be correct. It need not increase the size of the representations, nor must it emit a single unnecessary instruction when compiled. There are plenty of languages that do this, Rust being a prominent example.
- jcelerier 6y ago> If it is part of the API of foo that `ptr` may be null, then foo must not dereference `ptr` without verifying that it is non-null. So the body of foo must be something like: foo(int* ptr) { if (ptr != NULL) { ... } } then what ? foo(int* ptr) { if (ptr != NULL) { *ptr += 1; // optional<T>::operator* would introduce a branch here ? *ptr = *ptr / 2 ; // same if(*ptr > some_constant) // same { ... } // etc etc } } and compiler optimizations can't always be assumed, for instance debug mode (-O0) performance does matter
- emtel 6y agoThat’s why I said properly implemented! Good optional implementations let you do the check once to unwrap the inner value, so you have one branch. In rust: fn foo(s: Option<&S>) -> { if (let Some(inner) = s) { // now you can access inner repeatedly // without branches } } (edit to fix syntax errors)
- kragen 6y agoDo you mean that the purpose of std::optional is to be faster than a pointer (which could represent absence as nullptr)? I would have thought it would be ML-style safety, but it seems like you're right. I guess a tagged union (or std::pair<T, bool>) would be faster—if the std::pair is a local variable, fields of the wrapped object will be at fixed offsets from your stack pointer or frame pointer, so you save yourself an indexed register load the first time you access a field of the object. That seems to have been the intended use. Why did they think this was a good idea?
- dataflow 6y agoNo, the purpose wasn't to be faster than a pointer. You can't even necessarily use a pointer where you can use std::optional; the value of std::optional is embedded in itself. One purpose would be type safety (and things revolving around that). Another would be to let you pass around an optional object just as easily as the underlying object. As to whether it's a good idea to have it at all, I'm not particularly fond of it personally either. I don't have specific issues with its dereferencing safety though. Don't confuse type safety with memory safety though. Type safety != memory safety != thread safety != ...
- kragen 6y agoAll good points. Thank you.
- bnurun 6y ago> not an innocent-looking convenience syntax for flirting with the UB I agree, but C++ uses this pattern in so many places that I think it is less confusing to be consistent. When I see a nice, concise syntax or function, I check cppreference for undefined behavior...
- account4mypc 6y agoboost::optional was designed to mimic pointers. Before boost::optional, some programmers would return a T* as a caveman's optional<T>. Boost wanted to preserve the syntax of *, ->, and operator bool. Dereferencing a null ptr is UB, so operator* for optional was the same. I agree it would be nice if you could get an assert in operator*. But you can already fire up a debugger or sanitize build and get a nice error message anyway, so it's nbd.
- Someone 6y ago> it would be nice if you could get an assert in operator* If, as you wrote, operator* for an optional that doesn’t contain a value is UB, it can do whatever it wants, including assert. I think the problem is that there’s an implicit requirement that operator* optionals that do contain a value is as fast as a pointer dereference. (Aside: reading https://en.cppreference.com/w/cpp/utility/optional https://en.cppreference.com/w/cpp/utility/optional, I wonder how one can misuse “When an object of type optional<T> is contextually converted to bool, the conversion returns true if the object contains a value and false if it does not contain a value.” to write obfuscated code or hide back doors using optional<bool>)
- account4mypc 6y agowell, the immediate problem is it's hard to rewrite the STL without resorting to ugly patches
- solipsism 6y agoEasily dealt with with a linter. C++ is not fundamentally a safe language. It's fundamentally a no-cost abstraction language. It's not a baffling design choice if you know C++.
- pjmlp 6y agoPity that linters isn't something most C++ devs feel like they need. https://isocpp.org/files/papers/CppDevSurvey-2021-04-summary.pdf https://isocpp.org/files/papers/CppDevSurvey-2021-04-summary...
- ttt0 6y agoAs far as I'm considered, the purpose of std::optional is so you don't have to allocate memory on the heap and then check for null pointer. I don't want it to throw exceptions, just like I don't want the language to check the validity of a pointer every time I want to dereference it.
- jcelerier 6y ago> The whole point of an optional type is to prevent accidental unchecked access to the value no ? it is to model the idea of "a value is there or is not there". definitely nothing more. If replacing a T* t by a optional<T>& incurs a meaningful performance cost (like a branch) then optional just won't be used
- dang 6y agoIt apparently didn't at the time either: Modern C++ Won't Save Us - https://news.ycombinator.com/item?id=19723066 https://news.ycombinator.com/item?id=19723066 - April 2019 (388 comments) Modern C++ Won't Save Us - https://news.ycombinator.com/item?id=19714204 https://news.ycombinator.com/item?id=19714204 - April 2019 (1 comment)
- hollybug 6y agono shit
- bnurun 6y agoThe deliberate omission of std::span::at() is annoying. The paper that introduced std::span was titled "span: bounds-safe views for sequences of objects," but there is actually no bounds checking in the standard. It's only true with debug builds that also use slow, debug versions of the STL, which have almost negligible value compared to just using sanitizers.
- cataphract 6y agoIt's interesting that one of the things C++ got right was having value semantics by default and most of these "problems" are the result of using either references or types that behave like dumb references/pointers such as std::string_view.
- nine_k 6y agoValue semantics as in "copy constructor for everything" is only helpful in limited ways. What is helpful is move semantics for anything mutable, which was introduced quite recently.
- 10000truths 6y agoC++11 has been out for a decade now. Move semantics are not much more "recent" than Python 3.
- AnimalMuppet 6y ago> There are significant challenges to migrating existing, large, C and C++ codebases to a different language – no one can deny this. Nonetheless, the question simply must be how we can accomplish it, rather than if we should try. Um... no. Not every (or even most) existing large C/C++ codebase should be migrated to another language. "But security vulnerabilities! And crashes!" I admit all that. But a program can be useful and therefore valuable, even if it crashes at times. Rewriting the program adds value only by removing crashes and exploitability. In many cases, that's effort that could be spent in more valuable ways.
- atdt 6y agoI read that sentence as applying to security-critical software. i.e., I don't think the author would insist that Quake II be rewritten in Rust.
- brundolf 6y agoQuake III already has been ;) https://immunant.com/blog/2020/01/quake3/ https://immunant.com/blog/2020/01/quake3/
- ncmncm 6y agoQuake III appears to be C code. The argument for (automatically) rewriting C or nearly-C to Rust is stronger than for C++. Curiously, they seem to have found just one, inconsequential, memory usage error in the entire, large C program. This calls into question the frequently repeated assertion that there is no substantial and correct C code.
- lmm 6y agoThis amounts to saying that we should accept codebases continuing to contain exploitable vulnerabilities indefinitely. Perhaps for codebases that have a finite expiry date that's tolerable, but for a codebase that's expected to be maintained indefinitely I don't see how it can possibly be worthwhile - a rewrite will be a one-time cost, whereas exploitation is an ongoing cost that will surely exceed the one-time cost eventually.
- joshsyn 6y agoi love C++, C++ is the future of startups.
- andyxor 6y agoC++ is a beautiful, pragmatic and very efficient language, just constrain your use of it to STL, and avoid Boost and other over-engineered dependencies. The Ockham's razor principle of code design is not only important but essential when using large mature languages that have many surprising and nuanced ways to skin a cat. Being too clever with C++ is a recipe for disaster. Avoid OOP (inheritance in particular) and the sacred art of templates metaprogramming, but also avoid the lowlands of using C++ as a version of C language (with malloc everywhere), and you shall be rewarded in this life or next.
- geofft 6y agoI'm not sure I understand your points about Boost - everything the article discusses is in the STL, is it not? I'm also not sure I understand your point about being too clever and template metaprogramming - nothing the article discusses involves template metaprogramming, and I'd say it's doing very straightforward things. Do you think some part of this is too clever? Also, I'm not sure I understand how to reconcile your "Avoid OOP" advice with the advice to use the STL. Doesn't using the STL mean making use of OOP?
- andyxor 6y agoSTL and OOP are orthogonal, e.g. you can use STL without ever using the concept of inheritance. Boost is too popular imo, and many people take it for granted when using C++ that it comes with this nice utility library to complement STL, kind of like another python package, no big deal. From what I've seen on multiple projects it creates an explosion of poorly understood (and often poorly implemented) dependencies which leads to maintenance nightmare, memory leaks and bugs, performance issues and production outages. If you can avoid using an external dependency such as a gigantic library full of experimental features created by thousands of C++ enthusiasts avoid it at all costs. as it tempts the team to use it left and right when it's not really needed, just like templates. Templates, if used without strictly complying to the KISS principle can get unwieldy, full of nuanced, intricate and unnecessary abstraction, and make it hard to understand and maintain the code C++ is a giant language and its use on a particular project has to be very tightly constrained to a very minimal set of features and dependencies.
- zone411 6y agoClang gives a warning for the first example: <source>:7:25: warning: object backing the pointer will be destroyed at the end of the full-expression [-Wdangling-gsl] https://godbolt.org/z/zTnjbhMqr https://godbolt.org/z/zTnjbhMqr
- TeMPOraL 6y agoYeah, and clang-tidy gives another; I just typed the first example and got both -Wdangling-gsl and "Std::basic_string_view outlives its value [bugprone-dangling-handle]" from clang-tidy. It doesn't invalidate the point of the article, that these things perhaps should be easier to avoid, or impossible to express. You can code safe-ish C++ with high warnings settings and enough liters and static analysis tools backing you, but it's not ideal experience.
- cflewis 6y agoI changed teams at work (Google) to one that uses a C++ codebase that has survived for 20 years. As someone writing C++ for the first time, clang-tidy has been huge. It stops so many screw ups before I get to the code reviewer. I've not found C++ as bad as I worried, partly because Google's style guide limits the amount of silliness you can do, partly because I have avoided memory issues by just never allocating to the heap using `new` with smart pointers, but mostly because Clang and Clang-Tidy have helped me avoid all the footguns. I wouldn't write a new project in C++ (especially outside Google where the package management ecosystem is non-existant), but I'm no longer of the opinion that I would never join a team just because their current codebase was written in C++.
- jvanderbot 6y agoIs there a safe subset of C++ defined somewhere? Can I turn off some of these ridiculous "features"? As we add so much to the language, the number of footguns increases quadratically.
- PEJOE 6y agoC is still a widely used and beautiful language. It is "safe," in the sense that the machine does more or less exactly what you ask it to. Allocate memory when you need it, and free it when you consume it / are done with it. You do not have to use these features if you do not want to. C++ is very powerful, and has an amazing world of libraries available, but sometimes I feel like it is at least two languages at the same time. For example, one benefit of using C++ over C is writing C style programs with modern libraries and convenient containers in the STL, but you quickly learn that C style programming isn't supported across the board, and hitting that wall can be jarring and unexpected (Looking at you openCV - just try to allocate space for a struct containing a cv::Mat)
- pjmlp 6y agoYes, C++ the good parts is known as C++ Core Guidlines, https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
- jvanderbot 6y agoThis is great stuff. Thanks! I sympathize that compilers do not do "enough" of this for us. But there's really no substitute for a good code standard and developer culture focused on quality.
- pjmlp 6y agoUnfortunely C++ inherited the perfomance focused culture from C and sees such kind of tooling as needless bloat, only required for developers that need an extra hand while coding. https://isocpp.org/files/papers/CppDevSurvey-2021-04-summary.pdf https://isocpp.org/files/papers/CppDevSurvey-2021-04-summary... If you check other well known survey among C++ community you will see similar results in static analysis tooling and coding standards adoption. So while, contrary to the C community, WG 21 members do strive to push the code quality forward and publish tooling that helps to enforce it, the adoption could be much higher than it actually is on the field.
- coding123 6y agoI used to write a lot of C++ (like 14 years ago at this point). I haven't really paid any attention to it in that time. I am looking at the code examples and what I see is a language trying to adopt features from Java, Rust and others - but where in those other respective languages there's like just 1 thing going on in a single line of code. In these C++ examples you have the namespacing stuff, templates/generics, implicit operators and constructor calls. It just feels like exactly what you would expect of a language like C++ to try to adopt the native features of these other languages. Which isn't a bad thing, but it just screams to me like wouldn't it just be easier to switch languages? That all being said, I do get the "rush" C/C++ programmers feel when they write multi-threaded socket servers. You step back and go whoa - that worked?
- ncmncm 6y agoIt is, in fact, never a surprise when a modern C++ program runs the first time without faults. That is an important difference from C, and a quality it shares with Rust. What is missed by the authors of all articles of this kind is that, while all of the errors cited are possible, none are tempting. For example, while it is easy enough to overindex an std::vector<T>, the language provides a "for" statement that exactly walks the vector with no possibility of overrun. Using an empty std::optional<T> may seem like a danger, but nobody uses one unaware that it might be empty; it needs different syntax to look into than a regular value. std::string_view and span, similarly, may seem foolishly risky, but they are always safe when passed down a call chain. One does not find them returned from functions in a responsibly designed codebase. C++ is a box of sharp tools that a responsible engineer can use to make fine, reliable software. We have, today, many, many times more responsible engineers writing good C++ code than we have Rust coders in total. Work making C++ code safe and reliable thus has a much bigger impact on the world than anything ever done with Rust. That will be true for a long time to come. That is not a reason to avoid Rust, but it is a reason to remain respectful in interaction with those engineers.
- jandrewrogers 6y agoModern C++ is extremely powerful, more so than any other systems language, but not simple. I’m not even a fan of the language per se, but there aren’t any alternatives currently that are as powerful or expressive and I actually use that power pretty regularly. You can’t express modern C++ in other languages with similar code gen in remotely similar lines of code, which is its unique strength. In my experience, if you use the full idiomatic toolbox of modern C++, code mostly works the first time. That was never the case when I was writing C, or even legacy C++ (which was terrible).
- Animats 6y agoMy comment for too many years is that C/C++ fails to deal with three issues: "How big is it", "Who owns it", and "Who locks it". C++ has, with difficulty, made progress on "How big is it" through templates, but raw pointers keep leaking out. "Who owns it" has been tough to deal with, although "owned pointers" at least try. "Who locks it" has yet to be addressed at the language level, although at least there's now some agreement on multiprocessor semantics at the language level. The old line was that locking is an operating system problem. After years of C++ and six months of Rust, I don't think C++ can catch up with modern languages with template gimmicks. Getting pointers right needs global analysis. Getting decent error messages about inconsistencies between point A here and point B way over there requires global analysis. Trying to build a borrow checker with the C++ template and type system is pounding a screw. Rust has its own problems. Figuring out how to do something safely can be quite difficult. It can involve solving puzzles, and often involves rewriting things at several levels, especially when threads are involved. As a result, "unsafe" is too often used as an escape hatch when someone can't spend the time to get it right. Those who slave under the whips of "agile" may be forced to such hacks.
- shinazueli 6y agoI was with you until the last sentence. I write Rust code at scale, with schedule and performance constraints. I’ve literally never had to use the word unsafe in my code. If you can’t figure out how to do it without fucking up the borrow checker, that’s because you can’t figure it out, and you need to learn how to do your job. You’re never “forced” to hack anything. Blaming “a whip of agile” is just bad taste. Don’t be bad. Like, wtf, you had a decent comment and then shat the bed right at the end.
- creata 6y agoI'm not sure what you mean by "global analysis," but Rust's borrow checker doesn't do any analysis that I'd consider "global."
- WalterBright 6y agoD's ownership/borrowing system does data flow analysis within functions, i.e. it's intra-function. Inter-function (global) is handled via the function signature.
- whatever_dude 6y agoModern C++ is "arranging chairs on the Titanic". Don't remember where I read this (twitter?) but it feels so true. Sure you can get a better chair arrangement with enough effort, but it doesn't matter since the boat is sinking.
- crazypython 6y agoDlang (https://dlang.org https://dlang.org) avoids all these mistakes through careful language design and compiler-enforced checks.
- klibertp 6y agoD is garbage collected language. I've spent the past weekend writing some toy programs in D, and it was very pleasant experience, mainly because of the GC - I didn't have to think about allocating arrays or strings, I could just use them. There are ways to circumvent the GC, like RefCounted, but the baseline is different in D and C++.
- ppg677 6y agoYet C++ is what Google, Facebook, Microsoft and others still use for their most mission critical software.
- madhadron 6y agoMission critical software projects, by their nature are started rarely and make very conservative language choices. By its nature, much of it is old, so it's in C++. There's also a fair amount of it in C# and Java, too.
- MontagFTB 6y agoWasn’t a lot of Facebook’s initial implementation php? I seem to recall they moved off it, or wrote a specialized compiler to speed it up.
- wilsonthewhale 6y agoWeb logic is in Hack (offshoot of PHP), but all of the surrounding infrastructure has always been heavily skewed towards C++.
- eximius 6y agoYet they're also actively exploring alternatives. (Go, Rust, Haxe, etc)
- 01100011 6y agoWhen you're writing systems software you need systems software engineers and most of them are far more competent in C/C++ than, say, Rust, Ada, Go, etc. We choose C/C++ because it's what we know and what our colleagues know, but I think a lot of us wish there was a better alternative.
- jandrewrogers 6y agoNot to take away anything from your post, but Go is not a systems language. No language with a mandatory garbage collector can make that claim since it makes some systems code effectively impossible to implement.
- FpUser 6y agoThose C++ bad, complex, unsafe etc. etc articles are starting to get boring. If one wants to shoot him/herself in a foot it is fine. C++ offers countless possibilities. It (and plethora of libraries) also offers quick way to write sophisticated and performant applications without much fuss. Make your choice. I personally use C++ to great advantage and find it very productive and safe. And while being good programmer / designer I am not a C++ expert. Far from it. Or use language of your choice. Nothing is wrong with it. We do not have to live in the world where "there can be only one".
- dbrueck 6y agoIn a way, I agree. To me, C++ is an absolute train wreck of a language and choosing it for a new project borders on malpractice. But if people want to use it and it doesn't affect me, there's a limit to how much energy I'm willing to spend trying to talk them out of it... especially if they are a potential competitor, in which case I might nod encouragingly when I hear they're using it.
- FpUser 6y ago>"...choosing it for a new project borders on malpractice..." That's one healthy and balanced approach.
- ncmncm 6y agoAnd, I will nod encouragingly, knowing that you will spend hundreds of times as many hours waiting on builds as I will spend finding and fixing any bugs using your compiler might have helped avoid; and, knowing that you will find overwhelmingly fewer experienced coders available to help when you need them. In the past decade, I have spent more time on filing compiler bug reports than I have on tracking down and fixing memory usage errors. Rust does not solve a problem I have. But its Node.js-like dependency milieu worries me. That said, I wish you good fortune with your choice. I do not doubt you will find it. But if you do, it will be a result of your work, not your choice of language.
- adev_ 6y ago> To me, C++ is an absolute train wreck of a language and choosing it for a new project borders on malpractice. Personnally I tend to think exactly the opposite. And choosing a brand new hype language because "it's shiny and fun" on new projects that will have to be there for 20 years is just a sign of being immature and a malpractice. There is no guarantee your shiny language will be alive nor even supported on my next gen platform. When for sure, the good old safe set: C++, C, JS, python, Java will be there and alive, even in 20 years from now. And the result you will probably struggle and spend more time to get your nice shinny Rust code running correctly on iPhone/Android 20/NG-Cloud than you will ever to debug a damn core file in C++. To comment ironically on your post. It is more to "zealots" and "evangelist" like you that I personnally refuse to talk. These people are often more interested with playing with the last fancy tech available than producing anything useful and sustainable in their work.
- baybal2 6y agoI care about speed. Moderm C++ has a giant lot of features, slowly approaching to eclipsing the number of such in Java. I don't know how fast they work, how optimisable they are, and how much memory they eat. Speed is the main quality of a computer program.
- HexDecOctBin 6y agoSpeed is a misnomer there, leading to "Java is faster than C" type of benchmarks. You (and most experienced programmers) care about determinism. If we can fix the minimum and maximum, we can make it work; otherwise, beware all ye who enter here.
- detaro 6y agoLuckily, you can learn things you don't know. It's often necessary when trying to write fast code.
- goodoldneon 6y agoDepends what you're working on. That's probably true with games but the most important quality of most programs is maintainability.
- DethNinja 6y agoI love C++ and I’ve to admit that it will need a subset language soon but the examples given in this article wouldn’t possibly exist in well checked code-bases because you can immediately see usage stinks just by looking at it. I think every C++ is bad article can be summarised like this: 1. C++ has lots of features. 2. Let’s nonsensically combine these features to shoot ourselves on the foot. 3. Uh oh, C++ didn’t help us write good code. Hence, C++ sucks. People have to accept that in real life there is a thing called code-reviews and senior engineers are supposed to prevent such badly written code.
- Silhouette 6y agoPeople have to accept that in real life there is a thing called code-reviews and senior engineers are supposed to prevent such badly written code. Why should people accept that? There is overwhelming evidence that such reviews do not reliably prevent such problems from making it into production. In some cases, other languages exist that do reliably prevent the problem from ever making it into production because the problem is impossible by design. We can and should consider from time to time whether the advantages offered by an old but established language are now outweighed by the advantages offered by a newer but better designed language. If that doesn't happen with increasing frequency as time passes, we have a serious problem as an industry.
- jandrewrogers 6y agoI think it is fair to point out that it requires a comparatively high degree of skill and experience to use C++ well relative to many other languages. Most people do not use C++ well because doing so is quite difficult and requires a large investment of time. This is not helped by the fact that it has an enormous amount of legacy baggage that is technically valid code that no one should ever use — there is an entire anti-language you have to learn too. Its standard library has many flaws such that many experienced programmers eventually write and use their own alternatives to many parts of it. These are all legitimate hurdles and criticisms, it is neither a pretty nor easy language. The major benefit of C++ is that with sufficient mastery you can do complex things strictly, safely, and concisely that are difficult-to-impossible in any other systems language due to its flexibility and metaprogramming facilities. Its flaws are very real, but so are its strengths.
- wdpk 6y agothe problem I see with the author's very narrow point of view is that Rust, C++ or C are just tools which can all be misused and produce defects when used improperly or without a good understanding. Rust borrow checker can be implemented in a C++ compiler on a subset of the language which is ironically also what rust does, unsafe parts of Rust well are still unsafe... Already C++ compilers and static analyzers (thank you llvm) got surprisingly good at detecting memory unsafe issues (the author's string_view dangling reference for instance). To really solve a broader class of issues (including logical issues), in 2021, I am frankly way more excited by languages with advanced type systems (for instance Idris) than by Rust.
- sudopluto 6y agoHonestly Rust to me feels like a go-ish version of C++ with a modern compiler that has -Wall -Werror enabled by default
- wdpk 6y agoAgreed! It's a tough world out there in the fight for relevance. Besides Go and Rust in the recent languages that more or less clamor to compete with C++ are Swift, Nim, Zig, D etc. and honestly I don't know if Rust or any of those will save us from human error or incompetence.
- anfilt 6y agoI hate how people group C with convos like this. Sure it has problems, but does not have any to the insanity that C++ can, let alone hiding it like C++ can. There is a reason people jokingly say C++ can blow your leg off, while C is just a foot gun. C++ is just a massively bloated language. If you need native code just use C, Rust or even assembly. Stay away from the C++.
- jandrewrogers 6y agoC++ is a massively bloated language. But it is also vastly more expressive and more safe than C, and also significantly faster at runtime. It just has a stupidly high barrier to entry.
- anfilt 6y agoMaking a language more expressive at risk of making bugs less obvious is not a good thing. The goal with expressive syntax is to make code easier to read/write, not deceive the developer. Also C++ does not have a faster runtime than C. C has hardly any run time requirements. Mainly just setting up the stack, compared to C++'s runtime requirements I don't see how anyone could claim the C++ runtime environment is faster. Anyways, my point is that C and C++ have diverged far enough these days bundling them together is not really productive nor accurate.
- flohofwoe 6y agoC++ cannot be safer than C as long as it remains backward compatible, which would mean becoming a fundamentally different language. You can't fix a leaky pot by adding another leaky pot on top (err, or something...). Also "significantly faster at runtime" seems like a stretch. C++ doesn't have any fundamental features over C that would affect performance (for instance, it doesn't fix the pointer aliasing problem).
- zabzonk 6y agoThe poster child for C++ being faster than C is std::sort() vs qsort(). The latter requires a function pointer dereference for each call to the user-supplied ordering function - the former does not. There are many similar opportunities for C++ to be faster than C.
- deleted 6y ago[deleted]
- deleted 6y ago[deleted]
- deleted 6y ago[deleted]
- deleted 6y ago[deleted]
- CyberRabbi 6y agoNo programming language will save us. Hyperbole harms the discourse
- 127 6y agoHere's the point of view from someone who has only written 1 piece of production software with both C++ and Rust. Trying to write C++ I was constantly fighting accidental memory copies. With Rust all of this was trivial and everything works as I expect. This is pretty much the reason I chose Rust over C++. As a beginner I have no idea how I would begin to elegantly write immutable data, parallel code. With Rust it's just .iter() to .par_iter() (everything is immutable by default). C++ package management is awful (it has none). C++ headers are annoying and pointless. I know these are strong statements and of course only my opinion but I just see people claiming C++ is fine just having a strong case of Stockholm Syndrome. Rust is nowhere perfect. Often it can look like line noise, the project folder takes multiple gigabytes even for small-ish projects, the compilations can be slow, the tooling is not always there. This is still multiple orders of magnitudes better than trying to Google how Cmake works step by step. On the other hand I love rustanalyzer + vscode. It was super easy to get going with and just works. Visual Studio C++ seems to be much more steep as a learning curve.
- iudqnolq 6y ago> the project folder takes multiple gigabytes even for small-ish projects This is a workaround but you can specify a global directory for all those artifacts with the env var CARGO_TARGET. This will deduplicate common dependency versions and lets you stick it in a temp disk/exclude from backups.
- echelon 6y agoC++ is a garbage language. There's literally nothing worse. Not PHP, not Perl, not Bash nor VBScript. C++ is literally the worst language. You have to be an arcane wizard to write safe C++. Your code reviewers have to be all-seeing oracles. You're asked to follow this pattern called RAII, but the language does nothing to tell you this or enforce it. Creating declarations and header files is so ceremonious and tiring that it's a pain to prototype. By the time your ideas are concrete, you've already introduced bugs. Anyone who says they can program C++ safely is either a robot or is lying. Mozilla's single most important contribution is Rust. It's a literal godsend for systems programming. The ecosystem is maturing more and more every year, and it's now just about time we put C++ out to pasture. There's no saving it.
- jb1991 6y agoMany of the comments here are focusing on Rust as the obvious alternative, but it's great that the article twice also mentions Swift and Rust together as the obvious choices. If Swift ever gets more adoption outside Apple's platforms, it will be a game-changer. It's a very elegant language with much stronger typing than C++. It feels like writing in a scripting language but retains the type and memory safety.
- vineek 6y agoI completely agree with the points from this article. It is fundamentally infeasible to make all C and C++ code safe. The main reason, PAST code. There are countless lines of unsafe code already written. However, there is nothing magical about Rust's facilities (e.g. borrow checker) that make them inapplicable to FUTURE C and C++ code. I believe this is the only way forward for C and C++. They need to offer mechanisms that guarantee that any code written today can be provably memory and thread safe. That of course requires yet another tool in the myriad of tools that exist for C and C++.
- wdpk 6y agoby the same token you could say it's also fundamentally infeasible for Rust to be code safe, otherwise they would not have unsafe code... so in the end it's an article that says nothing interesting at all
- vineek 6y agoThe ability to occasionally opt-out is both a feature and a necessity because certain systems are inherently unstable and offer varying guarantees. I’m arguing for an opt-in feature in C++. Of course there is no panacea.
- brundolf 6y agoLanguage semantics matter. If a language doesn't place very strong meaning on certain things from the get-go, no amount of static checking will ever be able to guarantee certain traits of the code written in it, because it doesn't have enough information to work off of. And I'm not just talking about undefined behavior. People have been writing safety checkers/linters/etc for C and C++ for decades. Many of them are very impressive and useful. None of them can ever be totally sound, as a fact of the languages themselves.
- synergy20 6y agohttps://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines For new code stick to the core guidelines to make life much easier. I think c++ is imperfect, but it is de facto the best/fastest/most-powerful language in existence right now.
- quantgenius 6y agoC++ is a systems programming language. Just like C, it allows you to do anything you want to do and be as close to the hardware as you want to be. This is completely unnecessary if you want to build something that displays pretty pictures on the screen in a web browser, and you really should not use C++ for a web back end unless you have really hard core performance requirements. But when you need to get close to the hardware and don't want to write assembly, your only real choices are C and C++. To meet it's design objectives, C++ NEEDS to allow you to do things that are unsafe and it's up to you if you want to use them or not. std::span literally exists only to allow aliasing and the documentation warns you that it's up to you to to ensure that there aren't lifecycle issues. If you want lifecycle managed, don't use std::span, use copies but then you have the performance hit of copies. If you don't want either, use one of the libraries that handle reference counting for you but then you have the overhead of reference counting. If it's important not to have ANY overhead and are certain there are no scope issues, how else would you handle it? The difference between std::span and a raw pointer is mainly a) documentation for programmers reading the code and b) to make it easier for linters to find issues with how std::span is used. Similarly, if you want to capture by reference in a lambda, go right ahead but then ensuring that the underlying objects don't go out of scope is up to you. Use something like Rust or <insert language de jour here>, and you either have to lobotomize the language and give up the "safety" benefits the proponents talk about. If you don't lobotomize the language you either can't get what you want done or you give up a massive amount of efficiency. When the rubber actually meets the road, and you try doing things like introducing Rust or <insert language de jour here> to something like drivers for the Linux kernel, you immediately run into serious problems that show you why systems programming languages are needed. See for example the recent discussion about introducing drivers written in Rust into the Linux Kernel. There are tools to do fairly hard core checking of C++ code and if you really must you can write your own for things specific to your project using the clang libraries which you can call even from python and which do much of the heavy lifting for you. These tools just aren't built into most compilers and aren't required by the standard. They are however part of most serious development workflows. Further, if you actually make the effort to learn the language, code that looks remotely like what is written for the blog post is going to set off your spidey sense in about a millisecond. Nobody said using C++ was easy. The fact that it scares of dilettantes, and that when a dilettante makes it onto the team, their lack of skill is immediately obvious in their code is a feature, not a bug. It ensures that code quality and team quality remains high. C++ (and C) is designed to solve hard problems or achieve performance you can't with other languages. Hard things are called hard because it's difficult to do them. Otherwise they would be called easy. If you aren't even willing to read the documentation for std::span or lambda captures or plausibly read them and choose to use them in ways the documentation tells you will cause problems for a blog post, you are just wasting everyone's time. Note that there is in fact a recommended subset and set of code guidelines of the C++ language that it's suggested be used for new projects. Still under development and heavy revision but already very useful. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines. clang-tidy has options to check for conformance to these and much more. The Core Guidelines aren't going to allow you to start writing production quality C++ in a day or anything like that. There will still be a steep learning curve. But if you setup your linters properly and read the Core Guidelines (and a couple of good C++ books) you won't be quite as bewildered about how to start.
- torrent 6y agoThe typical, well known blog pattern for writing against C++: Show some buggy code an then conclude that C++ is wrong. Instead of asking: Who is wrong, the programmer trying to speak the language or the language itself? As always: Only use what you understand how it works. Instead of directly escalating it to cardinal questions of language superiority he had better used his time constructively and wrote a tutorial on how to correctly use things like string_view and span, that are a typed reference to memory that exceed the live time of the string_view.
- OneWingedShark 6y ago> Who is wrong, the programmer trying to speak the language or the language itself? This is more complicated a question than you think. If the language is such that you always have to look up some particular feature, then it's likely the language is wrong in that the designer chose that syntax poorly. OTOH, if the programmer just "threw something together" that happened to be incorrect, then obviously the programmer. This latter example is actually where having good error-messages can come in: pointing out what is wrong, and perhaps some relevant location in the language-definition. — Compare and contrast Ada and C++ error-messages (for "typical implementations") here as an example.
- expnkx 6y agoThis was a troll blog. Cannot believe people keep beating this dead horse. This is a troll post to promote Rust. We are not doomed. There are no such thing called "save" That Rust zealot claimed in the other blogs that using C or C++ violates human rights. He claimed memory safety issues in Rust are impossible however https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=rust https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=rust shows there are a huge amount of memory CVEs in rust. Sure modern C++ does not prevent all memory safety issues, neither Rust did.