14 ms·
C++ Papercuts
- lelanthran 3y agoFunnily enough, when I joined a C++ team about 4 years ago I could never get any senior dev to move away from using references instead of const pointers. Reference parameters are awful; the reader has no idea whether the argument will be modified or not without reading the header file. With const pointers you never have this problem.
- pjmlp 3y agoI do a mouse over and move on. Hardly any different from "var" parameters in plenty of languages since the 1960's.
- shadowgovt 3y agoIt's cool that you're using development tools that successfully parse the C++ you're working on. I found that to be a rare circumstance in any complicated code base. And once you lose that capacity, oh boy does this language become terrible fast.
- fsloth 3y agoI think you need to define what is a complicated code base here. Visual Studio works fine for millions of lines but I am sure the difference in experience can vary wildly.
- shadowgovt 3y agoVisual Studio crashes on the code base I maintain. It's a monorepo using bazel to manage it, and between the sheer number of includes compilation units take, the hundred thousand plus compilation units themselves, and some decisions made early on that are hostile to VS code tooling because the original developers didn't use VS code (such as hiding template implementations in .inl files that are included in the corresponding .hh file), VS code is functionally unusable for us. In general, the company moves forward because most of the same people who established the code base are still working here and they just already know all the quirks and can Intuit argument types, often because they wrote all the classes in question.
- fsloth 3y agoOuch, my condolences.
- ftrobro 3y agoVisual Studio != VS Code. I've always found Visual Studio to be great at C++ so I'm interested in knowing which of the two you meant?
- shadowgovt 3y agoVSCode, to be clear. The fact that Microsoft released two products in the same domain with almost the same name consistently trips me up. No, we don't spend money on code editors around here; I meant VSCode.
- TillE 3y agoVisual Studio Community is free to any company whose annual revenue is under $1 million USD. It's fully functional, unlike the ancient Express versions.
- fsloth 3y ago"No, we don't spend money on code editors around here; I meant VSCode." Not spending money in tools sounds like a very strange economics for software development. If interviewing does not scare you I warmly suggest finding a new employer.
- CyberDildonics 3y agoVisual Studio is not visual studio code. Also if you have a hundred thousand compilation units, it might be time for some shared libraries instead of one monolithic program.
- shadowgovt 3y agoOh I completely agree, but I am a mere senior software engineer and I have not yet figured out how to chisel past the corporate cruft of the three folks in charge of the codebase who believe monorepos are right because Google is doing it, therefore it must be the best way to do things. Nevermind that I have some experience with how Google did it, and it turns out there's a lot of ills you can solve with a 25,000+-strong engineering force that you can't in a smaller org. VSCode at Google never worked for me either, but punching a novel symbol into company-wide syntax-intelligent internal code search and glancing at the first response, which was likely to be the right one because it was a Google search engine so had well-tuned signals for "things humans care about," always did. They're not interested in how Google does it, merely that they do it so you can't get fired for making an architectural decision that worked for Google. Nor are they really interested in solving the tooling issue because it works for them; they just grep-and-pray. They're very fast at it because it's all they know so they've gotten quite good at it (when they have to do it at all; often they just know the types because they wrote the classes. ;) ).
- pjmlp 3y agoVS, QtCreator, Android Studio, KDevelop, C++ Builder,...are some of the names that come to mind where this herculean task is possible.
- shadowgovt 3y agoAnd they are, indeed, very impressive. But the nature of the language makes their task extremely difficult, and in my experience they aren't reliable. Besides the issue of C++ being just hard to compile (global symbol mutation via the macro system, the way templates instantiate, the compilation unit as the fundamental element of compilation meaning any individual compilation unit can require hundreds to thousands of include files as input), the language is rife with undefined behavior and assistive tooling is allowed to crash on undefined behavior. So, yes, if your tooling crashes it may indicate a problem with your code---but how are you going to find it now that your tooling has crashed? We've had a couple of initiatives at my organization to try and get tooling operational across the code base. They haven't stuck. Developers keep having to fall back to grep and pray. I prefer working with languages where there are simply fewer features and less undefined behavior. It makes the tooling more reliable.
- pjmlp 3y agoFrom other comments, it doesn't seem the organisation is willing to adopt the right tools, naturally one gets what one doesn't pay for.
- shadowgovt 3y agoIndeed, although I don't know what we could throw money at that would fix the problem of "Clangd crashes trying to provide LSP services for this codebase." We could buy Visual Studio and roll the dice and hope that it's more robust than vscode, but I have no particular reason to believe it would be. This organization has its work to do. My advice to other teams starting out is "C++ may be cheap in terms of hiring people and libraries that exist, but consider the cost once your codebase gets large. This is not a language that lends itself to safe behavior or tool-comprehensibility, and if that's something your project needs, be prepared for that. Other languages are safer, other languages are simpler for tooling to digest."
- fluoridation 3y agoI recently worked in a fairly large Rust project that I could simply not load into VS Code. rust-analyze would allocate more and more memory until it eventually crashed. I have never seen that with C++ code, besides that time I was investigating how someone was using CImg and found out that the CImg developers thought it was cute to make a single 3 MB header, and that brought the debugger to its knees. It still didn't crash, though! So yeah, difficult-to-parse codebases are not an exclusive feature of C++.
- shadowgovt 3y agoInteresting. This is the first I've heard of a Rust codebase too big to parse, but I'm not deeply surprised; Rust tooling hasn't been hammered on for as many decades as the C++ tooling. I'm medium-level confident there's meat on the bones of optimizing it to a point better than the C++ tools because they made choices in the language that do some amount of compartmentalization, so you don't get that "one #define up in this header you've never seen changes the meaning of symbols in every other file in this compilation unit." Unless that is possible and I simply haven't gotten deep enough into Rust to realize it yet.
- fluoridation 3y agoFunny you should mention that. I'm pretty sure the reason this project was so difficult to process was the (IMO) abusive use of macros. I'm talking foo!( in one line, and several hundred lines below was the closing parenthesis.
- shadowgovt 3y agoMacros (and in C++, their neighbor templates, since both are ways to make conditional decisions about what code to generate evaluated at compile time) are definitely the way to test out the limits of the compiler's performance. I haven't done enough Rust yet to hit this, but I don't doubt it happens. I did once make vscode scream on a very intricate family of conditional TypeScript types... Code compiled fine, but the editor started to take dozens of seconds to determine on-the-fly if types were sound. Back in the day, I once worked with a C++ game engine where the developer was obsessed with interfaces vs. implementation. Every class, even if there was one implementation, had an abstract interface wrapper. It was the first codebase I ever encountered where the compiler (on my Mac laptop) choked on it because it ran out of RAM trying to compile it.
- nly 3y agoUse a decent IDE. Most render & at the call site so you know. I find this all a bit rich anyway. In languages like C#, everything is a bloody reference and this lame concept of "reference types" corrupts developer minds.
- pjmlp 3y agoJava or Python would have been a better example, as that isn't the case with C#.
- raincole 3y agoI'm pretty sure one of the biggest differences between C# and Java is that in C# you can't assume everything is a reference. A strict is passed by value.
- fluoridation 3y agoNo, strings are passed by reference, it's just that the string type is immutable. Passing a string to a function doesn't create a copy, but there's also no way for the callee to modify the object that the caller points to. It is true that C# has value types that are by default passed by copy, though.
- raincole 3y agoI mistyped struct as strict.
- lelanthran 3y ago> Use a decent IDE. When I'm navigating code, the references aren't a problem, because I am using an IDE of some sort, usually. The complaint I made was about reading, not navigating, for example when reviewing a PR.
- uxp8u61q 3y agoUse a decent IDE that allows you to review PRs from within the IDE.
- ant6n 3y agoCant u use a const reference?
- dureuill 3y agowith const pointers you have the different problem that you need to define what happens when the nullptr is passed. While I would certainly prefer the reference to be visible at the call site (a mistake Rust did not repeat), if I have to choose, I prefer the inconvenience of having to use an IDE like qtcreator that will display references in a different style, to introducing an invalid value that needs to be handled (and will be mishandled). So yeah, as a senior dev, it checks out.
- TonyTrapp 3y agoHow does a const pointer express this in a way a const reference can't?
- dtho 3y agoIn the absence of IDE features, you can't differentiate between value arguments and reference arguments unless you view the callee's prototype. Pointers do not have this problem.
- TonyTrapp 3y agoIn the absence of IDE features, you also can't differentiate between a function taking a pointer and a const pointer. You must be aware in all cases that the value that you pass to the function may be modified. Yes, the function would not be modifying the pointer itself, but the outcome is exactly the same. A reference is pretty much equal to a non-nullable pointer in this context.
- lelanthran 3y ago> In the absence of IDE features, you also can't differentiate between a function taking a pointer and a const pointer. So? It still helps me with decoding `foo(bar, baz)`, as without the `&` I KNOW that bar and baz would not change after the call. And since both bar and baz are in the local scope, I already know whether they are pointers or not. With pointers, all the information necessary to determine if bar and baz would change after a call to foo is in the local scope. With references that information is elsewhere.
- gumby 3y agoA pointer can be nullptr while a reference cannot so that would be a step backwards. Also the “problem” you describe is not solved by your proposed “solution”. A const pointer can point to something that can be modified. You can declare a pointer to a const object, or, you know, declare a const ref.
- shortrounddev2 3y agoMicrosoft created SAL so you can annotate pointers to instruct the compiler to put up warnings or even errors for null required fields: _Success_(return) //out parameters should be set if this returns true bool tryGetValue( _In_ Map const* map, //pointer is for input only and cannot be null _In_ char const* name, _Out_opt_ Data* value //pointer is for output and can be null ); If you pass null for _In_ params, the compiler will warn or error. You return true without setting the _out_opt_ parameters, the compiler will warn or error. You can pass null to _out_opt_, and the compiler will warn or error if the function body doesn't properly check if the parameter is null
- fluoridation 3y agoOf course, that only works if you're passing a literal null. If you're passing a variable the compiler will not check anything, so this doesn't really solve the problem.
- jprete 3y agoThe annotation does need to be propagated backwards to callers and declarers - and there needs to be a way to "annotate" an existing pointer in the scopes where you've proven that it's not null - but that's better than having no annotation at all. (Although this is something I'd 100% want C++ to do, and not with an annotation but with a very short operator or reserved word.)
- lelanthran 3y ago[EDIT: Brainfarted in the last paragraph, read "const ref" as "ref"] > A pointer can be nullptr while a reference cannot so that would be a step backwards. Backwards from what? A reference can't be null, but it can still be invalid, which is a more common problem than forgetting to check for null-ness. > You can declare a pointer to a const object, Yes. I should have said that instead. > , or, you know, declare a const ref. That doesn't solve the original problem with references, though. I hate reading `foo (bar, baz)` and having to dig into the header to find out that bar or baz or both might be modified after the call to foo. When reading code, I want to be able to skip over anything that does not modify the state of variables in the current scope. Using references means that I have to know what every single call does. Using pointers exclusively, I can simply skip calls to functions with arguments that aren't pointers.
- MereInterest 3y agoWith const pointers, you have no idea if the parameter is implicitly optional, if it must point to a valid object, if it will throw an error when provided with a null pointer, if it will segfault when provided with a null pointer. Not even reading the header file will help you here. With const references you never have this problem.
- alexvitkov 3y agoYes, but non-const references are utter trash since you can't tell you're passing a mutable reference when looking at the call site, so for the non-const I'll always pass by pointer. Const references are less offensive, but I'm not going to pass non-const by pointer and const by reference, that's just inconsistent for no reason. If null is not a valid value, throw an assert at the beginning of the function. It's not like references can't be in a broken state, they're just syntax sugar for pointers (that also happens to wildly complicate the language's type system)
- ninkendo 3y ago> Const references are less offensive, but I'm not going to pass non-const by pointer and const by reference, that's just inconsistent for no reason. Why use pointers at all? Just use const references for immutable stuff, regular references when you want mutation, normal values when you want copy semantics, and smart pointers for other cases (unique_ptr for move semantics, shared_ptr for lack of clear ownership, etc.) If you want to represent nullability, std::optional is a good choice. > If null is not a valid value, throw an assert at the beginning of the function Yikes. This means the compiler will not catch it for you. Why make this a runtime issue? Use the type system to your advantage.
- alexvitkov 3y agoIf references were guarenteed sane and consistent values like in Rust, sure - but it's trivial to get a bogus reference in C++, so I don't buy the type safety thing. An assert actually catches errors, a reference just declares intent. As for smart pointers there's a lot of domains with tight performance requirements where you can't afford them - and to be quite frank, if you can afford to use shared_ptr you can also afford GC, so just use a sane language
- ninepoints 3y agoIMO read the damn header file and abide by the api contract. A const pointer type suggests nullability, and possibly even pointer arithmetic is permissible.
- fsloth 3y ago"get any senior dev to move away from using references instead of const pointers." ... "the reader has no idea whether the argument will be modified or not without reading the header file." It sounds like the problem is not using an IDE. Use references whenever posssible. Then you don't need to do a null pointer check. C++ is a footgun. And like with all guns, security first. With guns - put the safety on. With C++ - check the pointers are not null. OFC one can argue that with proper instrumentation invalid pointers are captured sooner than later, but the fact is it's much easier to represent invalid program state using pointer than references.
- thecodedmessage 3y agoYes, the "C++ is not usable on its own and needs other tools" defence of C++ :-)
- PaulDavisThe1st 3y agoIn my experience (31 years of C++) it is much more likely that problematic pointers will be non-null but also invalid.
- gpderetta 3y agoIf you pass a pointer (or anything with reference semantics) by value you can still get non local mutation through it without obvious call site syntax. There is no perfect solution. Avoid out parameters if possible, otherwise make the semantics clear via function naming or at least parameter naming conventions.
- YesBox 3y agoI use this solution as a solo dev: #define $ And add the $ in front of all non-const pass by reference parameters in the call
- diath 3y agoWith regards to multiple return values in "By-Reference Parameter Papercuts", you can make it more ergonomic with initializer list and structured bindings without having to use std::make_tuple and std::tie: auto get_multiple_values(const bool condition) -> std::tuple<bool, int> { if (condition) { return {true, 5}; } return {false, 0}; } void foo() { auto [status, value] = get_multiple_values(true); }
- NotCamelCase 3y agoWhat's the rationale for auto return type deduction and -> declaration at the same time?
- Chabsff 3y agoConsistency. The rationale is: "always use auto as the return type, and add a postfix type if inference can't do the job for a reason or another."
- ninkendo 3y ago> add a postfix type if inference can't do the job for a reason or another Ehh... not putting a return type in at all is a bridge to far IMO. Be friendly to the casual reader of the code, and help them understand what the function returns. (Yes I know IDE's can do this for you, but not everyone uses an IDE, and not everyone is reading the code in the context of an IDE... for instance code review, etc.)
- unkulunkulu 3y agoEhh… not using an IDE is a bridge too far IMO. Be friendly to the casual writer of the code and help them express themselves without having to repeat the obvious. (Yes, I know, we write less time than read, but maybe that is what is actually wrong with the tools we use?) Wrote this for myself, I’m a vim user :)
- usrnm 3y agoI don't know. I switched to go a year ago after 10+ years of C++ and, if anything, I realized that C++ was not so bad. Not bad at all. It's old and overly complicated, but it's also very powerful and even beautiful at times. Go is just bad, the runtime is great, but it's attached to a slightly upgraded version of C. It's not even much safer, I've seen more leaks in this year than probably 5 years of C++ before that. I know, the post is about C++ and Rust, but I'm tired of this "C++ is abandonware, just switch to a modern language" narrative.
- pjmlp 3y agoYeah, Go is a better C alternative in terms of language space. If one cares about language design beyond the ideas explored in Oberon and Limbo, not so much.
- zare_st 3y agoIf you mean Go is closer to C than Rust is to C, I guess that's correct. But neither are alternatives to C.
- pjmlp 3y agoThey certainly are, outside the religious UNIX and embedded groups that cannot grasp anything beyond C.
- lelanthran 3y agoYou keep going on as if C programmers only use it because they are not as smart as you, who punts C++ all the time. I wonder if you are unable to understand that for some people and some problems, having fewer footguns is a benefit.
- pjmlp 3y agoLots of experience meeting such folks for several decades, since 1993, to be more precisely. Lots of Usenet discussions on the matter, and other places.
- j1elo 3y agoBack in 2015 I read Effective Modern C++ by Scott Meyers [1], which is his latest entry in the saga of "Effective C++" books. It covers all the new stuff that got into the language with C++11 and C++14, and shows how modern C++ can be written. My feeling during the read, and my definite conclusion afterwards, is that I didn't read a collection of new nice additions to the language, but a list of minefields and new fancy ways to crash your program. That there is no chapter which doesn't end up being a list of gotchas. Seemingly every new feature was added as an extension of the language's reputation of being a shotgun that loves your feet. "Oh, now we have lambdas! here are the 32 ways you can fail while using them, and the language will do nothing to help you prevent". Effective Modern C++ is a very well written book and very instructive. Its flaws are none of the book or writer, but of the language itself. [1]: https://www.aristeia.com/books.html https://www.aristeia.com/books.html
- limaoscarjuliet 3y agoAgreed 100% The last time C++ "made sense" to me was 1998 reading and practicing Bjarne Strostrup's C++ book. It was better C then and a lot of these improvements made sense.
- bowsamic 3y agoYeah, Effective Modern C++ has always read like a list of warnings. C++ is kind of like Python in that the design of the language does not steer you into making good, idiomatic decisions. No one is going to guess most of the stuff that is taught in that book without reading it first from working with the language or reading other people's code, and really a full list of such things for C++ could be many hundreds or even thousands. I also agree that the new features seem to embrace and extend the "footgun-ness" of C++, rather than attempt to fix it.
- DougBTX 3y agoWhich book would be the equivalent to an Effective Modern Python?
- 3y ago
- RcouF1uZ4gsC 3y ago> In C++11, there is no ergonomic way to do a lambda capture by move – which is usually how I want to capture variables into a closure > It is unergonomic to return multiple values by tuple in C++. It can be done, but the calls to std::tie and std::make_tuple are long-winded and distracting, not to mention that you’ll be writing unidiomatically, which is always bad for people who are reading and debugging your code. C++14 allows you to move capture in lambdas. C++17 has structured bindings that help with returning multiple values. If you are claiming to an expert in C++ talking about paper cuts, at least be aware of things that have happened in C++ in the last decade.
- hardware2win 3y ago>C++14 allows you to move capture in lambdas. Your quote says cpp11 and youre arguing with 14? Wtf?
- leni536 3y agoThe limitations of C++11 shouldn't really be relevant today.
- deleted 3y ago[deleted]
- Night_Thastus 3y agoHahahaha. Oh that's a good one. Source: Someone who was, until recently, limited to C++03. Luckily we're up to 17 now, but occasionally 11 limitations creep in.
- thecodedmessage 3y agoAuthor of the blog post here. I am limited to C++11 for this, and will be limited to C++03 for some related projects. I did say C++11 in that comment, as I am of course aware that C++ has "fixed" that issue, but I'm still entitled to my gripes. I find it odd that someone claimed I was not aware the issue has been fixed in later versions, when I specifically said a version. People seem to be very motivated to assume I'm wrong about C++... because otherwise they'd have to admit that their favorite language has flaws. In any case, I revised that section to explain how even the "fixed" version in C++14 is still a paper-cut, and make the section hopefully flow a little better. ETA: I replied to the right comment. I'm agreeing with you :-)
- smallstepforman 3y agoWell, at least with C++ you can write linked lists and other algoritms which require multiple references. Not every engineer is sloppy with use-after-free, incorrect mutability, leaking resources etc. There are correct programs written in C++, the developers are not unicorns and it does not require super human effort. Kitchen knives are also unsafe and dangerous yet with their correct use we can create wonderful meals.
- pjc50 3y ago> There are correct programs written in C++ How do you know correct? Very few pieces of software are subject to full theorem-prover correctness, and very few pieces of C++ are completely free from ever invoking "undefined behavior" at runtime. Because that's extremely hard to do when even "x = x + 1" can be UB.
- AnimalMuppet 3y agoBy that standard, how do you know correct programs are written in any language? I seem to recall you praising JOVIAL a week or two ago; well, how many full theorem-prover correct programs were written in JOVIAL? I would guess, zero. Don't hold C++ programs up to a standard that programs in other languages also can't pass.
- pjc50 3y ago> I seem to recall you praising JOVIAL a week or two ago Think you have the wrong person? I have no idea what this refers to.
- AnimalMuppet 3y agoWell, I'll take your word on it. I was working from memory, not looking at a particular post. I apologize for the inaccurate claim. Back to the larger point: How many Rust programs meet your proposed level of "correctness"? How many Fortran programs, Java programs, Python programs? Virtually none of our software does. Why hold C++ to a standard that nothing else meets either?
- meindnoch 3y ago>There are so many features I miss from Rust, not only the obvious safety features, or even primarily those, but also features that C++ could easily add, like sum types (called enums in Rust), or first-class support for tuples. Stopped reading here. std::variant and std::tuple.
- jprete 3y agostd::variant is really cumbersome to use. When people talk about first-class support for tuples, std::tuple does not fit that bill.
- gwbas1c 3y ago> It can be done, but the calls to std::tie and std::make_tuple are long-winded and distracting Edit: It's really important to read criticisms of your favorite language. Often, it can help you find a better language for the task you're trying to accomplish.
- mempko 3y agoYou don't need to use tie and make_tuple. Another person posted a nice example.
- thecodedmessage 3y agoWhat part of "first-class" don't you understand?? ETA: std::variant is one of the least ergonomic user interfaces I've ever seen. std::tuple seems great until you realize that most PLs that support tuples just do "(,)"s for them, in creating, binding, and naming the type. C++'s versions are just unacceptable, and structured bindings only help on the binding side. First-class support for these is important. There's no reason they should be in the standard library rather than built into the language itself, besides historical mistakes.
- nwallin 3y ago> std::tuple seems great until you realize that most PLs that support tuples just do "(,)"s for them, in creating, binding, and naming the type. This C++ code compiles and does what a casual reader would expect it to do: #include <string> #include <tuple> using Foo = std::tuple<int, std::string, float>; Foo foo(int a, const char* b, double c) { return {a,b,c}; } godbolt: https://godbolt.org/z/Koa4zYT99 https://godbolt.org/z/Koa4zYT99 I gotta be honest, this interface is exactly what I would like a tuple API to look like. I want to declare my tuple as a `tuple`, I want to give it a friendly name, and I want to explicitly declare the types in the tuple, and I want to all of that at compile time. This API is exactly the verbosity that I want; I would be unhappy with either more or less boilerplate than this. > First-class support for these is important. Agree to disagree.
- kajaktum 3y agoI started writing C++ to do some JSON at work and it is absolutely horrible piece of shit language (even with nlohmann's and simdjson). I cannot believe people live like this. between figuring out CMake scripts, std::optional being basically useless. I never feel done writing code because I don't feel like I have: 1. Checked all the exceptions 2. Checked all the places where my std::optional instance is null (the checks doesn't propagate their information) 3. Checked that I am not doing any implicit conversion. 4. Really understand what happens at overflow? 5. Really understand if my code is even UB free? 6. Debugging is a mess, I can't step through code because the smartass compiler removes an entire segment of code even in debug mode. 7. I wish this garbage is written in C instead. At least its honest that its primitive and sinful, unlike this LARPING high level language. Only 2 things need to be true for this language to be permanently horrible: Hyrum's Law and backward compability. If it is possible to write bad code, then there will be bad code. Simple as. Doesn't matter how many guidelines you write or how many times you need to "teach" programmers or how many times you said people just need to be "better mkayyy?"
- jalino23 3y agoi was thinking of learning cpp for webgpu, this is discouraging
- kajaktum 3y agoYou can do it using whatever you want. I hope I don't discourage you from discovering C++. You might enjoy it, you might not, who cares. Go do it and make up your own opinion.
- pjc50 3y agoGood. C++ is discouraging. It combines huge complexity with readily available footguns, and the prevailing attitude is that all developers must be able to write perfect code with a full understanding of the language. Otherwise any security holes or runtime crashes are their fault.
- fsloth 3y ago
- gituliar 3y ago> Unfortunately, I am all too well aware of why these decisions were made, and it is exactly one reason: Compatibility with legacy code. C++ has no editions system, no way to deprecate core language features. Actually, I'm curious if somebody is working on a flavor of the modern C++ that abandons backward compatibility ? The Carbon project feels like a completely new language. What I'd expect instead is something that adopts best features and practices from modern C++, so that an average C++ project would require just minor changes to migrate, in spirit of Python 2 -> 3 migration.
- pjc50 3y ago> I'm curious if somebody is working on a flavor of the modern C++ that abandons backward compatibility ? This is just Rust. You can't really deprecate bare pointers in C++ without completely wrecking the language.
- HALtheWise 3y agoThis... seems false? In particular, the point of an "edition" system is that code written for the new edition can seamlessly import code written for the old edition, and so a hypothetical pointerless "new edition" would simply disallow bare pointers in new files, and things like smart pointers would continue to be _implemented_ in old edition code.
- pjc50 3y ago> would simply disallow bare pointers in new files And all their header files. That's a real Year Zero moment: you've just cut yourself off from the standard library, as well as most of the code you might want to link to. I don't think there's any "simpler" about it.
- steveklabnik 3y agoBringing editions to C++ failed, and I am not aware of anyone trying to tackle the issues https://github.com/cplusplus/papers/issues/631 https://github.com/cplusplus/papers/issues/631 (I could be wrong though! I follow the committee more than you may guess, but not as much as to think I know everything about what's going on.)
- andsmedeiros 3y agoI just don't use out parameters and avoid bare references altogether, const lvalue and rvalue references express idiomatically whether a parameter is to be copied or moved. Structured binding makes it trivial to consume returned tuples, so I don't find this a big issue.
- thecodedmessage 3y agoYou still have to write boilerplate to return them, though :-( But I agree it's better :-) Unfortunately, I'm on C++11 for this project, will be on C++03 for others :-(
- antoineMoPa 3y agoFor me, it's the fact that there is no standard package manager for the language. Finding the right linker/include args is soooo not user-friendly. Also, these `-I` and ˋ-l` flags can differ across platforms (and so can the installation procedure for these libraries). Yesterday, I tried to run an old project of mine developed on linux, but this time on a mac. First, the linker complained it could not find -lglm, I had to search a bit to find out that this is gl math. Then I searched this in homebrew, I also looked at different package managers. The low quantity of packages available surprised me. The most interesting package manager looked like it required python. Can't the community build a package manager in their own language? Anyway, at this point I gave up for the day. I love the language overall. Seems like standard & easier management of dependencies and compiler flags would help it thrive again.
- PaulDavisThe1st 3y agoYou can't have an effective package manager for a compiled language unless the package manager is actually a part of your own build system. The example I always use: you want to use libfftw ("Fastest Fourier Transform in the West"). But the library can be compiled with (at least) two options: use floats or doubles, use threads or not. That creates (at least) four possible variants of the final library. The only way to address this (and IIUC, Cargo for Rust does this) is to somehow have the pkg manager be a part of your build system, so that you end up with a local copy of the library, built as required.
- ho_schi 3y agohttps://mesonbuild.com/Wrapdb-projects.html https://mesonbuild.com/Wrapdb-projects.html Meson did that. You can use system packages, define your own dependency, use sub-projects (custom build option a, you mentioned) or readily available from WrapDB.
- tempodox 3y agoIf you download the sources, can't you compile them in whatever configuration you need?
- zoogeny 3y ago> There are so many features I miss from Rust ... like sum types (called enums in Rust) I feel the same whenever I have to go to other languages after using TypeScript. I want to model all of my data using the limited algebraic data type operations that many newer languages have (usually sum and product types). Recently I've been writing some Go for a project that is a tiny bit beyond a plaything. I am surprised how much I actually like Go and I am very glad to have come to the language to use for real after they added generics. But several times I wanted to do a union `SomeType | AnotherType` and the fact it is missing from Go is like an itch that can't be scratched. I would also add that discriminated unions alongside type narrowing (usually based on branching or escape analysis) just seems like something I want everywhere. Add in a powerful `match` statement and some kind of destructuring and I start to get very interested. If there is some future where Go picks up sum types, adds some kind of support for discriminated unions and type narrowing based on branching - then it might just be the best general purpose language (although unlikely to be a contender for best low-level systems level language).
- Night_Thastus 3y ago>const is not the default While I agree to a point, I also understand the argument against this. In programs, a lot of state needs to be changing. Which means you're going to type mut a LOT over the course of a 100k line program. And frankly, that gets tedious really quickly. If you miss a const in C++, no big deal. The program still works. It's mostly a safety net. [with notable exceptions] But if you forget a mut in Rust, the program is not going to work. That's an advantage (you can't write something incorrectly) but it's also tedious, imo. When working on the C++ codebase daily, I occasionally find myself going "Hmm, that could be const/I need const here to use something" and swap it over. But it's fairly rare, and overall less time investment on my end. Frankly, this explains most differences in Rust and C++. Rust is OK with the developer having to do more boilerplate, more verbose code, etc. for the sake of ensuring correctness. C++ assumes the best and goes with it to save some time in the short run. That's neither good nor bad, just preference.
- bombela 3y ago> That's neither good nor bad, just preference. I would argue that one of them giving you a better chance at producing reliable software should go above and beyond the notion of preference.
- Night_Thastus 3y agoSomething const would catch is rarely the cause of broken software. In my experience, most issues in software are logical and often derived from complex behaviors and interactions, not misuse of syntax.
- Measter 3y ago> While I agree to a point, I also understand the argument against this. In programs, a lot of state needs to be changing. Which means you're going to type mut a LOT over the course of a 100k line program. And frankly, that gets tedious really quickly. It may not be as large a proportion as you think. Doing a quick grep on the rustc compiler source (483k SLoC), there are 5431 matches for "let mut [\w\d_]+ =", and 29520 for "let [\w\d_]+ =". This is, of course, is only simple bindings and misses out on more complex patterns along with if-let and match expressions, but even so, about 84% aren't mut bindings. One of my own codebases (including all its Rust dependencies is about 1.5m SLoC) comes to about 80% not being mut. Ripgrep + dependencies (718k SLoC) is about 77% not mut.
- deleted 3y ago[deleted]
- TinkersW 3y agoThe post seems like it is focused on some ancient version of C++, multiple of these papercuts aren't even valid today. * You can just write = default for those methods often enough, and having to implement these is extremely rare * use [[nodiscard]] for your silly bool get_message return * C++ does in fact support multiple return types via structured bindings, or just use a struct None of this is really what is wrong with C++, the real issues are that everyone uses a different build system and a different way of sharing code.
- thecodedmessage 3y agoNo, you cannot use '= default' for the assignment operators if you have custom copy/move constructors. I thought this was clear that this was my complaint in the post. I will make it clearer in the post. IF you customize the constructors, THEN =default will break your code, even though the correct implementation is entirely boilerplate. And for tuples, you still need to use std::tie or std::make_tuple on the other end.
- FpUser 3y agoAll valid points. I agree that C++ as a whole is insanely big and complex and I can't even dream nor that I want to know about every nook and cranny. None of this however precludes me for writing well behaved software using subset of modern C++ that I actually need. The good thing is that when I need some esoteric feature that will help me to to this and that - it is always there and is quickly searchable thanks the the Internet. I prefer wide array of C++ choices especially when it comes to supporting various paradigms and techniques to a stricter set in Rust for example. I do see that real C++ experts (I am not the one) can get frustrated with the language but I think we have different goals in general. Being a programmer I do run my own business and I believe ROI wise C++ is more beneficial for my situation (I do use multiple languages when needed / requested by clients).
- Night_Thastus 3y agoExactly. Just use what you need. Only use the big hammers when absolutely needed. That's the power of C++ IMO. Lots of big hammers out there, but lots of smaller ones for when you don't!
- nayuki 3y agoI agree with the blog post, also having learned C++ followed by Rust. Only in the context of saner modern languages like Rust did I see C++'s footguns in retrospect. Probably my top footgun for C++ is implicit copy construction by default. Every class gets a copy constructor by default unless it opts out. When a copy constructor exists, it can be used implicitly and silently for casting and other contexts. Implicitly copying big objects like std::string or std::vector can be a huge, silent waste of time - whereas in Rust you must explicitly call `.clone()`. > const is not the default Agreed with this footgun. The old C and C++ ways are to be permissive by default, to be strict after adding more words. As another example, top-level variables and functions are exported by default unless prefixed with `static`. Whereas in Java, Rust, etc., you need `public`/`pub` to export, nothing to keep private. Another huge footgun is the collection of all the undefined behaviors from integer overflow to null dereferencing to creating out-of-bounds pointers to buffer overflows.
- nwallin 3y ago> Side note: Someone brought up structured bindings in a comment, as if this fixed the issue. Structured bindings are a great example of the half-way fixes that proponents of modern C++ love to cite. Structured bindings help some, but if you think they make returning by tuple ergonomic, you’re mistaken. You still need to write std::pair or std::make_tuple in the function return statement. No you don't. A tuple needs to be declared once. Then everything else is sugared. #include <string> #include <tuple> using Foo = std::tuple<int, std::string, float>; Foo foo(int a, const char* b, double c) { return {a,b,c}; } Foo bar(long); double baz(long x) { const auto [a, b, c] = bar(x); return c; } godbolt: https://godbolt.org/z/exdsb4hqb https://godbolt.org/z/exdsb4hqb Note that the return part of this (foo()) worked as far back as C++11. Only the structured bindings on the receiving side (baz) requires C++17.
- thecodedmessage 3y agoThanks! It’s still not as ergonomic as full first class support, but I did have the situation in my head as worse than it was. This has been fixed :)
- 1n3nt0r 3y agoI've used C++ from its inception (when it was cfront C++ -> C translator) to modern C++, not to mention many other languages over the years. Totally agree with the author that C++ manages to accumulate many bad decisions over time, leaving a legacy of overly complex or under-powered features. I suspect a certain laziness in not tackling some of the harder problems in the compiler, for example: type inference, improving the grammar to disambiguate new keywords from variables (for example co_yield). Many features could have been improved with more effort and willingness to tackle hard problems. Perhaps if Sean Baxter's Circle (C++ language extensions) gains traction, there will be hope to modernize C++ syntax and features.
- vonwoodson 3y agoAnother love letter to Rust is always welcome. I'm extremely pleased to see another compiled language that makes developers so excited to write code. I do think that it's entirely possible, though, that the warts that Rust does have will start to surface in time. There are no silver bullets.