[HN Gopher] C++23: Removing garbage collection support
___________________________________________________________________
C++23: Removing garbage collection support
Author : nalgeon
Score : 184 points
Date : 2023-11-01 13:35 UTC (9 hours ago)
(HTM) web link (www.sandordargo.com)
(TXT) w3m dump (www.sandordargo.com)
| afavour wrote:
| TL;DR:
|
| > Garbage collection and related features are being removed from
| C++23. If you are surprised to learn that the C++ standard had
| support for GC, you are not alone. It was unimplemented,
| confusing and pretty useless hence it's removed.
| pipo234 wrote:
| Good riddance!
|
| So the proposal was sponsored by HP, Symantec and Intel. I was
| expecting something related to Microsoft and it's ill conceived
| idea of "Managed C++", but it all boils down to: Hans Boehm.
|
| As surprised as the next guy to find this in C++ standard.
| ...Again: good riddance.
| AdmiralAsshat wrote:
| Garbage Collection is not a bad idea, but, my takeaway is
| that if you try to bolt it onto a language that didn't have
| it in its core design, you're gonna have a bad time.
| repelsteeltje wrote:
| It's rather that with RAII (C++, Rust) you don't _need_ a
| GC.
|
| Of course, you can still create and operate a GC arena in
| C++, to manage a data structure or problem area where it
| makes sense. But why build in into the language, library,
| runtime if developers don't need it?
| simiones wrote:
| (Well-designed, first-class) GC is still the only system
| proven to completely remove memory safety issues. Rust
| comes close, but it's not RAII but the compiler-enforced
| ownership tracking which allows for this.
|
| C++ is very much proven to be memory unsafe in practice
| despite the RAII and smart pointers being part of the
| standard for a decade or more now. Opt-in memory safety
| basically just means no memory safety.
|
| Of course, this doesn't mean that you can't create memory
| safe C++ programs. Probably one or two actually exist.
| But there also exist memory safe C programs and probably
| somewhere there is some memory safe program written in
| assembly as well.
| repelsteeltje wrote:
| > (Well-designed, first-class) GC is still the only
| system proven to completely remove memory safety issues.
|
| To be pedantic, you need more than a GC for memory
| safety. Like a language (type system, bounds checks) that
| forces you to only look only inside the memory being
| managed. :-)
|
| Also, "for free" memory safety as offered by java's GC
| doesn't automatically guarantee resource safety. Without
| RAII, you still need to manage you file handles the old-
| fashioned manual way.
|
| Beyond that there are safety concerns that where the
| shared memory model managed by the GC bites you. For
| example: Rust solves threading issues with move semantics
| and pinning, guiding programmer away from shared mutable
| state. Also think of stop-the-world problems inherent
| with GCs. They can be life threatening in real-time
| systems.
|
| So: yes it's true that it's difficult to write safe code
| in C++, and many safety bugs are related to memory. But
| that doesn't mean that code in a GCed language is
| inherently safer.
| vips7L wrote:
| > Also, "for free" memory safety as offered by java's GC
| doesn't automatically guarantee resource safety. Without
| RAII, you still need to manage you file handles the old-
| fashioned manual way
|
| Don't you think a file handle is a completely different
| thing than what is normally talked about with memory
| safety? Can a un-closed handle let you ++ your way into
| remote code execution? Or reading uninitialized memory?
|
| Regardless, a GC is about trade offs. A GC alleviates the
| 99% of programming. Thinking about having to close a file
| resource occasionally, especially since javac will warn
| you when you don't, is leagues easier than having to
| navigate the borrow checker all of the time.
|
| The same goes for concurrent programming. 99.9% of all
| code written in GC'd languages like Java never go over a
| thread boundary.
| aidenn0 wrote:
| > Don't you think a file handle is a completely different
| thing than what is normally talked about with memory
| safety? Can a un-closed handle let you ++ your way into
| remote code execution? Or reading uninitialized memory?
|
| Unfreed memory also doesn't let you ++ your way into
| remote code execution.
|
| Using a file-handle after it's been closed can cause all
| sorts of issues; and if the underlying file-descriptor is
| being reused to e.g. write a shell script you can end up
| in RCE territory as well.
| vips7L wrote:
| > Using a file-handle after it's been closed can cause
| all sorts of issue
|
| Is this possible in managed languages like Java? AFAIK
| you'll get an exception which is a bug and can cause
| issues, but its not a safety concern.
| simiones wrote:
| > To be pedantic, you need more than a GC for memory
| safety. Like a language (type system, bounds checks) that
| forces you to only look only inside the memory being
| managed. :-)
|
| True enough :-)
|
| > Also think of stop-the-world problems inherent with
| GCs. They can be life threatening in real-time systems.
|
| There are GCs that can work in real-time systems, given
| enough resources. RAII is also not suitable for real-time
| systems without much care, so the point is pretty much
| moot. Real-time systems require a level of attention to
| timing that no regular programming paradigm enables.
|
| > So: yes it's true that it's difficult to write safe
| code in C++, and many safety bugs are related to memory.
| But that doesn't mean that code in a GCed language is
| inherently safer.
|
| It is very much clear that programming in a GC language
| is safer than in C++ (or C or assembly) - not _safe_ (the
| log4j vulnerability comes to mind), but absolutely and
| certainly safer. Rust has a radically new approach to
| memory safety that seems to offer similar advantages. It
| 's still a little early (not that much internet-connected
| Rust-based infrastructure) to say for sure, but so far it
| does seem to offer similar or better safety guarantees as
| a GC language.
| Dylan16807 wrote:
| > (Well-designed, first-class) GC is still the only
| system proven to completely remove memory safety issues.
|
| This depends on what you mean by "proven", but I don't
| think I agree. It's pretty easy to demonstrate that RAII
| without raw pointers and without multithreading is going
| to be unable to escape its bounds. And for C++ that
| includes changing the standard library so it doesn't
| generate pointers without checking, for example vector is
| entirely _capable_ of enforcing bounds.
|
| But as you say, opting in on a per-object basis is not
| going to work.
| dezgeg wrote:
| What would be your solution with regard to iterator
| invalidation?
| another2another wrote:
| I'd also argue that with well used RAII, programs can be
| written more reliably than in some GC languages as you
| have finer control over the lifetime of system resources
| like Windows HANDLES or sockets and can free them on
| scope destruct with a simple wrapper class.
|
| On .net I'm constantly fiddling with IDispose and
| checking each object if it needs a 'using' block so I'm
| not leaking resources.
|
| On long running resource intensive apps, this can make a
| real difference and I prefer the finer control C++ gives
| me.
| mikepurvis wrote:
| Interesting that GC was originally part of rust and got un-
| bolted:
|
| http://pcwalton.github.io/_posts/2013-06-02-removing-
| garbage...
| zozbot234 wrote:
| What's the formal difference between 'pointer safety' and
| 'pointer provenance' which is also a concern in recent C/C++
| standardization work? Perhaps the former is getting removed
| because it turns out to be a less precise duplicate of the
| latter?
| orangepanda wrote:
| Doesnt std::shared_ptr use garbage collection?
| zaphoyd wrote:
| std::shared_ptr is reference counted. No GC involved.
| cpitman wrote:
| No, it uses reference counting, internal to the shared pointer
| implementation itself.
| conradludgate wrote:
| It uses reference counting and RAII. It does not use
| reachability analysis or tracing. Many people refer to a
| tracing routine or reachability analysis when they refer to
| garbage collectors
| repelsteeltje wrote:
| No, std::shared_ptr is plain ref counting
| eatonphil wrote:
| If you subscribe to the unified theory of garbage collection
| [0] then yes shared_ptr does use garbage collection.
|
| See also Herb Sutter [1] (describing a different project, but
| also mentioning shared_ptr): > Q: "Is this
| garbage collection?" > Of course, and remember that so is
| reference counting (e.g., shared_ptr).
|
| [0]
| https://www.cs.cornell.edu/courses/cs6120/2019fa/blog/unifie...
|
| [1] https://github.com/hsutter/gcpp#q-is-this-garbage-
| collection
| simiones wrote:
| It's a matter of definitions. Technically, reference counting
| is a form of garbage collection, and it is often discussed in
| the garbage collection literature.
|
| Most commonly though, "garbage collection" refers to global
| schemes which apply by default to all memory (perhaps with rare
| exceptions such as pinning).
|
| So, in typical usage, C++ smart pointers, being opt-in, are not
| considered garbage collection. Languages which do automatic
| reference counting globally, such as Python or Swift, are
| indeed considered garbage collection schemes.
| Sohcahtoa82 wrote:
| > Technically, reference counting is a form of garbage
| collection, and it is often discussed in the garbage
| collection literature.
|
| This is something I always thought was true, but then I see
| people talk about reference counting VERSUS garbage
| collection, when I was taught that reference counting is a
| _subset_ of garbage collection.
|
| In other words, "garbage collection" merely refers to any
| method of automatic memory management that the programmer
| doesn't have to think about, and reference counting is merely
| one method of implementing garbage collecting.
|
| Heck, Python uses reference counting, yet the library for
| directly interacting with the reference counter is called
| "gc", for "garbage collection".
| simiones wrote:
| Different terms can have (slightly) different meanings in
| different contexts.
|
| As I said, I think in practice any form of memory
| management that the language runtime/compiler does for you
| is considered garbage collection in the colloquial sense,
| but any such system where you have to use a specific
| language construct (such as std::shared_ptr or Rc) are not.
|
| I'd also note that Python doesn't _just_ do reference
| counting: it also has a tracing garbage collector that
| collects unreachable reference cycles. I 'm not sure if
| Swift does something similar.
| jcranmer wrote:
| I would call a reference-counted mechanism automatic memory
| management. I would only call it garbage collection if there
| were a mechanism to try to collect cycles among reference
| counts.
| ReleaseCandidat wrote:
| Depends on your definition of "GC". But used in the "usual"
| way, it includes reference counting. Wikipedia agrees:
| https://en.wikipedia.org/wiki/Garbage_collection_(computer_s...
| KerrAvon wrote:
| This is a silly argument, though. GC has a clear de facto
| meaning. If you say "Garbage Collection" in a room full of
| programmers, they're going to assume the traditional meaning,
| as seen in Lisp and Java, where you have something capable of
| automatically collecting cycles without programmer
| intervention.
|
| I've seen people claim malloc and free are GC. And sure, you
| can get there, but it makes the term utterly meaningless.
| pjmlp wrote:
| Only if they are the kind of those that call themselves
| "engineer" without a professional Software Engineering
| degree.
|
| Apparently you aren't even aware there are Lisp and Java
| implementations with reference counting algorithms for
| garbage collection.
| Dylan16807 wrote:
| > Apparently you aren't even aware
|
| 1. That's a pretty ungenerous interpretation of "as seen
| in".
|
| 2. Are people expected to know about those specific
| implementations or they're a bad programmer? If that's
| your intent then screw off.
|
| Also that sounds like a broken way to do "Java" without
| qualifiers. For Lisp, eh, you can even use a bump
| allocator if you want but that doesn't mean everyone
| should consider it every time they talk about Lisp.
| pjmlp wrote:
| Yes, that is exactly my intent, thank you very much.
| Dylan16807 wrote:
| Do you realize how vanishingly few people are aware of
| those? Your threshold for expertise is self-centered and
| wrong.
| pjmlp wrote:
| As mentioned, such things are part of any Software
| Engineering degree worth taking.
| Kranar wrote:
| Not where I work. Where I work people are very well aware
| that referencing counting is a form of garbage collection.
| Any memory management abstraction that simulates an
| infinite amount of memory is a form of garbage collection,
| including a system that never frees memory (the so called
| null collector).
| wg0 wrote:
| I have this impression that modern C++ is far more complex with a
| far more large surface area (from the perspective of learning the
| language) than Rust.
|
| Because I am not expert in both, what folks that came from C++ to
| Rust or have to work regularly work with both say about that?
| Filligree wrote:
| Dramatically more so. Rust is complex, but most (not all!) of
| the complexity is there to support a specific, modern, safe
| style of programming.
|
| C++ adds fifty years of cruft to that.
|
| A lot can be said about the surrounding social environment, but
| as far as the languages go, I don't think it's far wrong to say
| that Rust is "C++: The good bits".
| ReactiveJelly wrote:
| Yeah I guess I'd phrase it as 3 kinds of "complexity"
|
| Rust is complex because the compiler has many ways to say "No
| I won't compile that, it might be wrong".
|
| C++ is complex because it requires the programmer to
| understand every feature used in the codebase. (e.g. Will the
| compiler warn me if I use inheritance and dynamic dispatch
| wrong? I'm not sure. I find code with inheritance hard to
| read.)
|
| Go _code_ is complex because the language is too simple. (if
| err != nil is waterbed complexity)
| Sohcahtoa82 wrote:
| > Rust is complex because the compiler has many ways to say
| "No I won't compile that, it might be wrong".
|
| Doesn't Rust have an escape hatch in the form of the
| "unsafe" keyword for cases where you're reasonably sure the
| code is safe and correct?
|
| I haven't used Rust and most of my knowledge of it comes
| from HN, so I could easily be wrong.
| Filligree wrote:
| It does, which is where the social aspect comes in:
| You're expected to make every reasonable effort not to
| use it, even if that comes at slight costs in
| performance, because practically speaking people are
| terrible at making those judgements.
|
| Sometimes works well. Sometimes, unfortunately, it leads
| to bullying.
| Sohcahtoa82 wrote:
| I always pictured that in a professional setting,
| engineering management would have an edict that `unsafe`
| is simply not allowed, or possibly, they allow it with
| extensive code review by multiple engineers.
|
| And that there would also be a lot of people trying to
| implement a double-linked list and finding themselves
| having to sprinkle "unsafe" all over the place to satisfy
| the borrow checker.
| marcosdumay wrote:
| > C++ adds fifty years of cruft to that.
|
| If "that" refers to the support of a modern, safe style of
| program, then no, C++ has only the cruft, not that.
| dthul wrote:
| I work with both, having started with C++ about 17 years ago,
| and agree that Rust feels like a relatively simple language
| compared to C++. Rust might feel harder to learn initially
| because the borrow checker won't let you compile certain
| programs, but once you are over this initial hump, the rest is
| quite straightforward.
| jbaber wrote:
| I keep selling it as "Like vim, there's just this one thing
| to get over, then everything's clear."
|
| https://jbaber.sdf.org/misc/editors/learning_curves.jpg
| raverbashing wrote:
| I guess it's safe to extend the analogy to "C++ is like
| Emacs" then
| fluoridation wrote:
| I don't really agree with that. I'd say they're complex in
| different directions. C++ has complexities Rust doesn't
| because it bends over backwards for source-level
| compatibility. A lot of it is entirely at the semantic and
| pragmatic level. Rust's complexities are mostly due to its
| type system, meaning the complexity is at the syntactic
| level. I had never seen a computer crash because an IDE was
| trying to figure out how to parse a program before I worked
| with Rust, for example.
| foooorsyth wrote:
| >I had never seen a computer crash because an IDE was
| trying to figure out how to parse a program before
|
| This happens daily on my Intel MBP in Xcode. In only a ~15k
| LoC small app, 99% Swift. I've had to break up several
| functions into smaller ones purely because the compiler
| chokes so easily. They actually have a dedicated error
| message to the tune of "couldn't figure out types here,
| could you break this function up please?".
|
| But yeah, outside of that I've never seen it happen in
| major languages using major language tooling. Never even
| saw it in 5 million+ line C/C++/.NET CLR mixed codebases.
| steveklabnik wrote:
| It is funny that you mention so specific a situation, as a
| very funny version of this was going around yesterday:
| https://developercommunity.visualstudio.com/t/Too-much-
| anime...
|
| It doesn't invalidate your experience, it's just a funny
| bug.
| fluoridation wrote:
| "Too much anime" was not a phrase I expected to see in a
| compiler bug report.
|
| Internal compiler errors do happen from time to time.
| They're annoying but usually easy to work around. I've
| had projects where I just _cannot_ use rust-analyze
| because it just never finishes. It just eats RAM without
| accomplishing anything.
| bluish29 wrote:
| >"Too much anime" was not a phrase I expected to see in a
| compiler bug report.
|
| Looking on the code example they provided [1], the
| comments is literally an anime character. Is he Naruto?
|
| [1] https://godbolt.org/z/1PGrYjq3h
| zogrodea wrote:
| That looks more like Goku or another Dragon Ball Z
| character. It's a pretty funny bug report for that fact
| though.
| GrayShade wrote:
| Blockchain? Did you file an issue?
| fluoridation wrote:
| Hah. Yeah. No, I didn't file an issue. I don't think it's
| a bug on rust-analyzer. There's only so much a language
| server can do.
| gpderetta wrote:
| I had emacs crash (well, lock-up) while trying to parse the
| error messages from some extreme C++ metaprogramming. It
| was at least a decade ago and both emacs and C++ have
| improved, but still...
|
| edit: mind, in the same code base (10M+ loc), Visual Studio
| would crash while attempting to initialize Intellisense.
| TeMPOraL wrote:
| > _I had never seen a computer crash because an IDE was
| trying to figure out how to parse a program before I worked
| with Rust, for example._
|
| C++ is complex enough that the IDE can't really parse much
| of the program's code in any useful fashion. You're lucky
| if it can get the right type hints and jump to definition.
| And even the latter may not be complete.
|
| Contrast with e.g. Java, which makes it easy for the IDEs
| to get the full picture.
| fluoridation wrote:
| Sure, but in those cases the parser just gives up. It
| doesn't grow its working set trying harder and harder
| seemingly forever.
|
| We're talking about C++ and Rust here, so I don't know
| why you bring up Java. If parsing Rust was as easy as
| parsing Java you would not see me complaining about it.
| TeMPOraL wrote:
| Giving up vs. crashing is a trivial difference,
| ultimately boiling down to an extra terminating condition
| connected to real-world resource use. Either way, the
| parsing is useless.
|
| I brought up Java as an example of what it means for the
| IDE parsing to work and be useful.
| fluoridation wrote:
| Losing your work versus a minor inconvenience is a
| trivial difference to you? Well, okay!
| TeMPOraL wrote:
| What kind of IDE are you working in that will lose your
| work when it crashes?
|
| I don't know what's going on in the Rust world, but in
| C++ world, even the good ol' Visual C++ (2017, 2019),
| when it crashes (which it does surprisingly often on my
| work's codebase), it doesn't lose anything other than
| maybe unsaved edits. It's annoying, sure, but 30 seconds
| later it's back to where it was before the crash.
|
| Also, a not working parser is _not_ a trivial
| inconvenience. It just means the tool doesn 't work. From
| the POV of wanting to use advanced functionality that
| relies on parsing the code, it doesn't matter whether the
| tool aborts its execution so it doesn't crash, or has to
| be disabled because it _doesn 't_ abort its execution and
| just crashes. The end result is the same: I can't use the
| advanced functionality.
| fluoridation wrote:
| What I said was that the _computer_ crashed. The IDE used
| so much memory that it took the system with it. When it
| came back up something weird had happened to the
| environment and it was ignoring the stuff in .bashrc.
|
| >Also, a not working parser is not a trivial
| inconvenience. [...] The end result is the same: I can't
| use the advanced functionality.
|
| Yeah. Now compare "the IDE is just working as a glorified
| text editor" to what I'm describing above.
| TeMPOraL wrote:
| I'm sorry for misunderstanding your earlier comment, and
| thank you for clarifying. I can see how this is a much
| more serious problem.
|
| However.
|
| That sounds to me less like an IDE problem, and more like
| a _Linux problem_. Specifically, the problem with the...
| unique way a typical Linux system handles OOM state, i.e.
| by suddenly curling into a ball and becoming completely
| unresponsive until you power-cycle it. I 've hit that a
| couple times in the past, and my solutions were, in
| order:
|
| - Monitoring the memory usage and killing the offending
| process (a graph database runtime) before it OOMs the
| system;
|
| - After becoming tired of the constant vigilance,
| quadrupling the amount of RAM available in the OS; (yes,
| _this_ is why people overprovision their machines
| relative to what naive economists or operations people
| may think..)
|
| - Changing the job, and switching to working on Windows;
| WSL may not be 100% like a real Linux, but it inherits
| sane OOM handling from its host platform.
|
| I'm sure there is a way in Linux to set a memory quota
| for the offending IDE process. This would hopefully
| reduce your problem to the more benign (if annoying) case
| I described earlier.
| fluoridation wrote:
| I actually run Windows mainly. This was inside a Hyper-V
| VM with 32 GiB of RAM. I'd like to be able to work on
| this project from Windows, but unfortunately I can't, and
| don't have the energy or inclination to figure out how to
| get it building on Windows. I already knew rust-analyze
| had this problem, which is partly why I allocated so much
| memory for the VM. Unfortunately I triggered a rust-
| analyze rebuild just as I was already building another
| codebase in a different directory. That's what brought it
| over the edge.
|
| While I agree that Linux sucks at handling this
| particular situation, my point was about Rust's
| complexity. _Normally_ , when you're using C/++
| dependencies the IDE doesn't need to inspect the
| internals of the libraries to figure out how to parse
| dependent code. And, it's also true that rust-analyze
| doesn't know how to stop trying. It _will_ parse what you
| give it, even if it kills your machine.
| layer8 wrote:
| C++ precedes Rust by ~30 years. Just wait how large the Rust
| surface area might have become in 2053. There's lessons to be
| learned from history, so hopefully less, but about every
| successful language so far has only kept growing and becoming
| more complex.
| hmfrh wrote:
| Surely the C++ surface area will also have increased
| significantly in the same span of time which means that Rust
| will still be ahead?
| bluGill wrote:
| Probably, but how much is an open question.
| AnimalMuppet wrote:
| By then, there may be a newer "new" language that is
| simpler than Rust.
| ReactiveJelly wrote:
| Indeed it's already grown `async` and `?` since 1.0. But I
| love those features, they're a good thing.
|
| In Rust, the compiler still checks everything. I can't mis-
| use async or the question mark operator and accidentally make
| my code unsafe.
|
| In C++, I'm expected to know everything about every feature I
| use, so I have to be paranoid. Sure I remember that
| unique_ptr isn't atomic, do my juniors remember that? Sure,
| returning a reference to a local variable is a warning, but
| it's not an error, right? Many less-healthy teams probably
| ignore those warnings. And I myself don't even remember the
| rules for SFINAE or `move` in full. Not to mention that
| OpenCV's mix of shallow and deep copying for matrices
| casually breaks `const`.
|
| In Rust, more surface area is just more Lego bricks. If two
| Legos snap together, you're safe. If they don't, don't force
| them. C++ expects you to force everything and take
| responsibility for not being a human encyclopedia of the
| language.
| no_wizard wrote:
| Is it not possible to use a linter with C++ that can tell
| you when you have done X or Y incorrectly?
|
| surely, there is tooling that can help right?
| thesuperbigfrog wrote:
| It is possible to use C++ with a linter to help detect
| errors, but linters do not catch all errors nor do
| linters always understand what you are trying to do.
|
| Most approaches to using C or C++ safely involve throwing
| out large portions of the language and disallowing
| features that are easy to misuse or problematic to
| analyze for safety.
| foooorsyth wrote:
| Not all of the issues you can create in C++ are visible
| at compile time. That's kind of the point.
|
| A lot of the time you're going to need to lean on
| Valgrind. And that's AFTER you shipped a fatal crash and
| you're parsing a tombstone.
| UncleMeat wrote:
| Often no, for multiple reasons.
|
| Separate compilation means that even if you write an
| interprocedural static analysis system (quite a bit more
| complex than what most people would call a linter), you
| still run into oodles of hard boundaries where you can't
| look into other functions. Fixing this requires explicit
| annotations all over the place to even have a chance.
|
| C++ is also a remarkably complex language in terms of
| aliasing relationships. There are a bazillion ways you
| can make two names alias each other. This gives you a few
| options when writing an analysis. You can be sound and do
| weak updates everywhere, which means your alarms will
| basically never be confident. You can be unsound and
| assume no aliasing, which means that you've got a lot of
| false alarms. You also can't really make a rule "don't
| ever create aliasing relationships" because they are
| often idiomatic C++.
|
| And finally, the key properties that people really deeply
| care about like heap lifetimes fundamentally involve
| complex reasoning about both temporal properties,
| dataflow, and heap shape. All of these are hairy static
| analysis problems. In combination, a nightmare.
| AnimalMuppet wrote:
| Almost every feature is a good thing. But the total
| quantity can become a bad thing, because the interactions
| between the features become more and more complicated. It's
| almost like features have values proportional to the number
| of features, but they have costs proportional to the square
| of the number of features. You eventually reach the point
| where new features add more cost than they add value.
|
| But it's not that simple, because each new feature adds
| value to a subset, but adds costs to everyone. If the
| subset is vocal, they often get what they want, even if
| it's a net loss for all users taken as a whole.
|
| So the trick is, first, to stop adding features once the
| costs outweigh the benefits, and second, given that you
| have only a finite number of features that you can add
| before you reach that point, to add the total set of
| features that are going to make the most valuable language.
| protomolecule wrote:
| "Sure, returning a reference to a local variable is a
| warning, but it's not an error, right? Many less-healthy
| teams probably ignore those warnings."
|
| Seriously?
| bluGill wrote:
| Rust has the advantage of seeing the mistakes of the past and
| not making them. Many intersting ideas have been tried, only
| after significant use do we discover which are good and which
| are bad.
|
| Compromise is sometimes needed. C++ had some ideas they knew
| at the time were bad, but backward compatibility forced it
| and backward compatibility is itself a great idea worth the
| costs.
| ttfkam wrote:
| One of those history lessons is that change happens. This is
| why Rust has editions: allows for new features without
| breaking old code. There is still code complexity, but that
| complexity has been shifted/amortized into the compiler suite
| instead of everyone's project code.
|
| Crucially, editions allow for deprecation, which is a trick
| C++ always had trouble with no matter how outdated the
| language construct.
| phkahler wrote:
| >> Just wait how large the Rust surface area might have
| become in 2053.
|
| This is a valid concern. One can hope that Rust evolves very
| slowly and as-needed. IMHO part of the problem with C++ is
| the fact that a committee exists to advance the language and
| produce regular updates. Combine that with most of the
| language already being defined (with a lot of overlap with C
| BTW) and you get a lot of bolted-on stuff and core features
| as part of the standard library that might have otherwise
| been part of the language with nice syntax. Rust had the
| advantage that a lot of things had been learned prior to its
| creation so things are cleaner. Lets hope keeping it that way
| is a priority and not just adding new things on top of new
| things - I think they're doing it right, but I don't really
| follow it.
|
| C is great in this regard. The language is IMHO mostly "done"
| and rarely changes. I'm happy to use C99 and not much demands
| newer.
|
| Looking at Wikipedia I'm afraid C is starting to get too many
| updates, but the 2017 version is said to add no new features!
| :-) https://en.wikipedia.org/wiki/C_(programming_language)
| marcosdumay wrote:
| Well though-out languages gain complexity in a much slower
| rate than the carelessly put together ones. By orders of
| magnitude.
|
| I doubt Rust will become as complex as C++ before it's
| abandoned in a few decades.
| staunton wrote:
| Why do you think it will be abandoned? Programming
| languages past a certain point don't die easily, especially
| when used for low-level system programming (otherwise C and
| C++ would be long dead).
| marcosdumay wrote:
| All languages stop evolving at some point. C isn't there
| yet, but there are many dead languages that people only
| use because a lot of things are already written on it,
| and nobody wants to change anything.
| staunton wrote:
| So why does that mean Rust will be abandoned in a few
| decades?
|
| Do you think there will be a trend "back" to using C or
| C++ for systems programming? I would bet against it. I do
| believe, by the way, that C has stopped evolving (which
| is good) and that C++ should stop evolving as well.
|
| Or do you think the replacement of old languages by new
| ones will accelerate? So in 30 years most systems
| programming will be done in a language that doesn't exist
| yet? Maybe not done at all in a "programming language" as
| we have today?
|
| Or do you think Rust is clearly losing out to some other
| new languages for systems programming, such as Zig, and
| will never be popular enough in the first place to enter
| the "slowly dying legacy" regime?
| alerighi wrote:
| I think the big problem with C++ was the "C" in it, that is
| maintaining compatibility with C (or sort of compatibility).
| Rust didn't make this choice and it's a completely new and
| different language.
|
| Bad choices in C++ will ever change since you will break
| compatibility with a ton of stuff. Rust has the concept of
| "edition" that allows to migrate to new language versions
| gradually.
| kitkat_new wrote:
| This. It always amuses me when people complain about complexity
| in Rust. It feels like they have no idea what they don't know
| about the alternatives
| gp wrote:
| C++ lets you write anything you can imagine, and the language
| features and standard library often facilitate that. The
| committee espouses the view that they want to provide many
| "zero [runtime] cost," abstractions. Anybody can contribute to
| the language, although the committee process is often slow and
| can be political, each release the surface area and capability
| of the language gets larger.
|
| I believe Hazard Pointers are slated for C++26, and these will
| add a form "free later, but not quite garbage collection" to
| the language. There was a talk this year about using hazard
| pointers to implement a much faster std::shared_ptr.
|
| It's a language with incredible depth because so many different
| paradigms have been implemented in it, but also has many
| pitfalls for new and old users because there are many different
| ways of solving the same problem.
|
| I feel that in C++, more than any other language, you need to
| know the actual implementation under the hood to use it
| effectively. This means knowing not just what the language
| specifies, but can occaissionally require knowing what GCC or
| Clang generate on your particular hardware.
|
| Many garbage collected languages are written in or have parts
| of their implementations in C++. See JS
| (https://github.com/v8/v8)and Java GC (https://github.com/openj
| dk/jdk/tree/36de19d4622e38b6c00644b0...)
|
| I am not an expert on Java (or C++), so if someone knows better
| or can add more please correct me.
| pjmlp wrote:
| There are Java implementations in Java like Jikes RVM.
|
| Garbage collected languages can be easily bootstraped, it is
| a matter of what intrisics are available, and what mechanisms
| are availble beyond plain heap allocation.
|
| Oberon, Go, D, Nim, Modula-3, Cedar are some examples.
| mauvia wrote:
| Honestly, there's a "lot" of extra stuff but the standards
| committee has a general standard that things that can be done
| in a library don't need to be supported on the language level,
| meaning 90% of the new stuff is going to be invisible to you
| except in making the language more ergonomic to use.
|
| An easy example is ranged-for. It depends on so much complexity
| internally that the end-user basically will never see. All
| they'll see is that as long as std::begin and std::end are
| defined for a container you can just `for (auto item :
| container)` it. Stuff like overload resolution is essential to
| it but you don't need to know overload resolution rules to use
| ranged-for.
|
| Or the way initializer lists make initialization so much
| simpler. The way you can leave constructor writing to the
| compiler for so many different types of constructors.
|
| How the compiler handles elision so well because of how the
| constructors are designed. You don't need to write a single
| rvalue reference move constructor for simple types. They're
| generated for you.
|
| The current ongoing push to make large parts of the standard
| library constexpr so you can have seriously complex things
| going on directly at compile time to result in a minimal output
| that can just put in equivalent constants to constexpr function
| calls.
|
| Like, the reality is, it is pretty much a core principle of the
| language that you can write C++2003 if you really want to, and
| everything on top of it just makes writing things easier. But
| if you want to piecewise substitute your code with the latest
| stuff it more often than not becomes terser and more
| straightforward because of the evolution of the std library.
| benreesman wrote:
| I've done a pretty fair amount of both, more C++ in total, more
| recent work in Rust.
|
| I think it really depends on whether you've got a mountain of
| legacy C++ with dated infrastructure and practices or a modern
| C++ code base at shop that runs a tight ship.
|
| In the former case the incidental as opposed to inherent
| complexity in C++ is a real PITA compared to Rust (which isn't
| exactly shy about gratuitous complexity especially in the trait
| system and going more than a bit overboard with macros IMHO).
| C++03 written without modern tooling and a hardass style guide
| and stuff is usually a nightmare. I would vastly prefer to work
| in almost any Rust codebase compared to a sprawling nightmare
| of code that still calls new/delete routinely.
|
| A modern C++ codebase with all the best practices and tools and
| stuff? 6 one way half dozen the other more or less: Rust is now
| fast and feature full enough to be an option for most anything
| C++ would do. Do you like hardcore affine typing by default or
| dislike it?
|
| Another way to think about it is that modulo _some_ big
| differences: Rust bundles (and mandates) a bunch of stuff you
| opt into in C++: a uniform set of best practices, hardcore
| static analysis, a credible strategy for catching memory safety
| issues and UB and thread safety issues. (The case is overstated
| about the difference in efficacy of e.g. ASAN and the borrow
| checker, they have pros and cons and it's not a 1-bit debate).
|
| C++ tooling has a few important edges (though Rust is catching
| up): clangd is usually (always?) faster and more stable than
| rust-analyzer but you can throw hardware at it so it's not a
| huge deal.
|
| Cargo is just a dunk below some project size. Above some
| project size the story is still evolving.
|
| It's just not as big of a difference as you often hear one way
| or the other. I'd probably default to Rust unless I had library
| interop reasons to go C++ (which is often).
| pavon wrote:
| This might be influenced by the fact that I've used C/C++ for
| much longer, but my impression is that Rust is an even larger
| language with more details to learn than C++. The difference is
| that in Rust if you get the details wrong (in safe code) you
| get a compiler error or at worst a logic bug, while in C++
| sometimes you get a compiler error and sometimes you get memory
| corruption or integer overflows, or undefined behavior.
|
| Expanding on this, the general concepts you need to understand
| for both are about the same, but because Rust enforces them in
| the compiler, you have to learn the detailed rules of how the
| language enforces ownership and lifetimes and such which is
| more detailed/complicated/restrictive than the concepts
| themselves.
|
| Furthermore, some of the Rust language details cause library
| APIs to become more involved than they would be in C++. An
| example in the std library is that in C++ handling errors and
| function results are orthogonal features. With sum types they
| are intertwined, and the Rust community is more liberal with
| adding convenience functions and syntax so you end up with a
| combinatorial explosion of all the different ways you want to
| handle the error combined with all the ways you want to handle
| the result, with about 40 methods each in Result and Option,
| plus methods for handling errors in iterators (functional
| streams). Lifetimes and async can also complicate crate APIs in
| ways that don't exist in C++. None of them are difficult on
| their own, but the shear volume of things you need to learn an
| remember makes me appreciate the minimal Go philosophy.
|
| On the flip side, the places where C++ gets more complex are
| all the little bad decisions that can't be fixed for backwards
| compatibility like the stupid numeric conversion rules
| inherited from C which can easily bite you. And both the C and
| C++ string/stream libraries suck in their own ways.
| trealira wrote:
| > This might be influenced by the fact that I've used C/C++
| for much longer, but my impression is that Rust is an even
| larger language with more details to learn than C++. The
| difference is that in Rust if you get the details wrong (in
| safe code) you get a compiler error or at worst a logic bug,
| while in C++ sometimes you get a compiler error and sometimes
| you get memory corruption or integer overflows, or undefined
| behavior.
|
| I disagree. Rust seems less complicated in many ways. For
| example, move semantics are a lot simpler; they're always a
| byte copy, whereas with C++, you have to remember rvalue
| references, lvalue references, and universal/forwarding
| references (which are usually rvalue references but sometimes
| are lvalue references). You also have to be careful not to
| mess with a moved-from object, as it's in an unspecified
| state.
|
| C++ also makes a distinction between trivially copyable types
| and non-trivially copyable types (a distinction Rust doesn't
| make). It's difficult to remember the rules for non-trivially
| copyable classes, but they boil down to "if it has a user-
| provided constructor, destructor, or copy-assignment
| operator; or if it has virtual functions; then it's not
| trivially copyable."
|
| In Rust, all you have to remember is that types are by
| default moveable (and moves are a memcpy), deep copies are
| implemented through the Clone trait, and if your type can't
| be memcpy'd (e.g., a self-referential struct) it needs to
| only be accessible through Pin pointers, which ensure that it
| isn't memcpy'd in safe code.
|
| Another thing: you can cause a use-after-free error by
| combining coroutines with lambdas [1]. An error like this
| only happens because C++'s rules around coroutines and
| lambdas are complicated enough that the committee didn't
| forsee this happening. This seems indicative of higher
| complexity in C++ than Rust, to me.
|
| [1]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95111#c23
| pavon wrote:
| On the other-hand, I find it annoying in Rust that an
| assignment or an unadorned function parameter might result
| in a move or a copy and you can't tell by the function
| signature or call site. Instead it depends on whether the
| type implements the Copy trait, which is a big semantic
| difference based on "spooky action at a distance".
|
| > You also have to be careful not to mess with a moved-from
| object, as it's in an unspecified state.
|
| This is a great example of Rust being easier, even when it
| just as complex. Both languages don't allow you to use an
| object after it is moved, so it is the same amount to learn
| and to think about when writing code, but Rust will give
| you a helpful compile-time error while C++ will let you
| blow your foot off.
| fluoridation wrote:
| >You also have to be careful not to mess with a moved-from
| object, as it's in an unspecified state.
|
| This is incorrect, somewhat. It's "unspecified" in the
| sense that the standard doesn't mandate that user-defined
| move constructors and assignment operators leave source
| objects in any particular state. All standard library
| classes are left in well-defined states when moved (if they
| can be moved), and you can choose to define your classes to
| do the same. The usual rule of thumb is that a moved object
| should be in the same state as if it had just been default-
| constructed. This is what all the standard classes do.
|
| Move semantics in C++ and in Rust are more or less
| equivalent. The major differences are that C++ copies by
| default and rust moves by default, and that Rust doesn't
| allow using a moved object while C++ does.
|
| >In Rust, all you have to remember is that types are by
| default moveable (and moves are a memcpy), deep copies are
| implemented through the Clone trait, and if your type can't
| be memcpy'd (e.g., a self-referential struct) it needs to
| only be accessible through Pin pointers, which ensure that
| it isn't memcpy'd in safe code.
|
| If defining value semantics was so simple you wouldn't have
| had to bring up Pin pointers (which I assume are not just
| pointers, but something special that needs to be kept in
| mind), or the fact that Rust understands that there are two
| different kinds of code, which C++ doesn't distinguish.
| It's suddenly so obvious that one is simpler than the
| other.
|
| >Another thing: you can cause a use-after-free error by
| combining coroutines with lambdas [1]. An error like this
| only happens because C++'s rules around coroutines and
| lambdas are complicated enough that the committee didn't
| forsee this happening. This seems indicative of higher
| complexity in C++ than Rust, to me.
|
| It's trivially easy to cause use-after-free errors with
| lambdas. Return an std::function<void()> that captures and
| reads a local std::unique_ptr by reference and then call
| operator()() on the object. This is not a complex interplay
| between features; lambdas necessarily introduce dynamic
| lifetimes into a language that was originally not designed
| to support them, so using them requires care.
| trealira wrote:
| >If defining value semantics was so simple you wouldn't
| have had to bring up Pin pointers (which I assume are not
| just pointers, but something special that needs to be
| kept in mind) [...]
|
| Pin<P> is basically a wrapper for any pointer type P,
| whether it's Box<T>, &mut T, &T, etc. Its sole purpose is
| to keep the object that's pointed at from being moved,
| byte-copied, or byte-swapped. For self-referential
| structs, that's all you can do; there's no equivalent to
| move constructors. See this link at the bottom [1].
|
| > This is incorrect, somewhat. [...]
|
| You're right, but I really just meant that you have to
| keep track of more. If you use a moved-from unique_ptr,
| for example, you'll dereference a null pointer. Sometimes
| you do want to use a moved-from object, though, so it's
| not like this can be disallowed. It's something you have
| to keep track of. I just think this is more complex than
| Rust's unconditional rule that moves are bytewise copies,
| and moved-from objects aren't allowed to be used. I've
| heard C++ programmers complain about Rust being less
| powerful than Rust in this respect, but it is simpler.
|
| > It's trivially easy to cause use-after-free errors with
| lambdas.
|
| I don't fully understand it, but from what's described at
| the link, this causes the use-after-free bug:
| [x] () -> future<T> { co_await something();
| co_return x; }
|
| The fix is to write it like this, I'm pretty sure:
| [x] () -> future<T> { auto xx = x;
| co_await something(); co_return xx; }
|
| The reason is because coroutines copy their input to
| their own stack frames, but lambdas are passed by
| address, and so the coroutine later dereferences a
| dangling pointer. Again, it just seems more complex than
| anything in Rust.
|
| [1]: https://doc.rust-lang.org/nightly/std/pin/index.html
| fluoridation wrote:
| >If you use a moved-from unique_ptr, for example, you'll
| dereference a null pointer.
|
| You can still check if the pointer is null first, which
| would be UB if the object was simply in an undefined
| state.
|
| >It's something you have to keep track of.
|
| You don't have to keep track of it, you just have to
| design your classes such that moving a value leaves the
| source in a usable state. At the point of use, moving is
| no different from any other operation on the object.
| There's no intrinsic difference between moving an
| std::unique_ptr and reset()ing it. You do have to keep in
| mind the possible values of the object as you operate on
| it, but this has nothing to do with move semantics. In
| any language, you wouldn't want to attempt to access the
| 10th element of a list after clearing the list.
|
| Now, if you design your class such that the moved-from
| state is invalid and distinct from any state the the
| object could reach by any other means then yes, you will
| need to treat moves on that type differently. However,
| that's a problem you created for yourself.
|
| >The reason is because coroutines copy their input to
| their own stack frames, but lambdas are passed by
| address, and so the coroutine later dereferences a
| dangling pointer.
|
| Yes, like I said, lambdas decouple lifetime from lexical
| scope. You have to be use them carefully to avoid running
| into issues. This is not because C++ lambdas are a
| complex feature than Rust lambdas. The opposite is true:
| Rust lambdas are more difficult to use incorrectly
| because Rust's type system is more complex and can keep
| track more closely of the lifetimes of objects.
| senderista wrote:
| > Move semantics in C++ and in Rust are more or less
| equivalent.
|
| That's not my experience at all. The big annoyance for me
| with C++ move semantics is that it forces me to allow an
| invalid or default state (representing a moved-from
| object), which subverts a major premise of RAII: non-
| default constructors establish class invariants which are
| maintained for the lifetime of the object, so that all
| other code can assume those invariants. There should be
| no such thing as an invalid or partially initialized
| object. When I'm forced to allow an invalid state to
| support move semantics, all my code has to either check
| for this invalid state (if it's logically possible) or
| assert that it's not present (if it's not logically
| possible). That's a major source of gratuitous complexity
| that simply isn't inherent to move semantics, as Rust
| demonstrates. (The invalid state is necessary because
| there's no way to prevent destructors from running in
| C++, so a destructor needs some way to know that an
| object isn't properly initialized.)
|
| C++ move semantics have brought us back to the bad old
| days of checking isInitialized flags and calling
| initialize() methods, which is what non-default
| constructors were supposed to solve.
| fluoridation wrote:
| If you find yourself doing if (!initialized)
| initialize(); that's a sign that you should have just
| called initialize() on the moved object while still
| inside the move constructor, if initialize doesn't need
| any additional parameters. If there's no way to construct
| or initialize the class in a default state (e.g.
| something equivalent to an empty std::string) with no
| additional parameters, it's probable that the class
| shouldn't have been movable, and instead the object
| should have been wrapped either in std::unique_ptr or
| std::optional. Not every class needs to be movable.
|
| Again, this has nothing to do with C++'s move semantics
| and everything to do with how you define your object's
| state transformations.
| senderista wrote:
| Let me make it a bit more concrete. I have a hazard
| pointer class, where the constructor registers the
| provided pointer for GC protection, and the destructor
| removes GC protection. I would like to be able to
| dereference this hazard pointer object freely, without
| doing null checks everywhere. RAII is the perfect fit for
| these semantics: the constructor establishes the
| invariant (GC-protected non-null pointer) and all other
| code can assume the invariant. Until I needed move
| semantics, that is.
|
| In the constructor of a class with a hazard pointer
| member I needed to be able to initialize a hazard pointer
| on the stack and then move it into the member variable.
| (Because the hazard pointer constructor is fallible, I
| needed to catch exceptions thrown from the hazard
| pointer's constructor and retry from within the
| containing class's constructor, so I couldn't just use an
| initializer list.) In order to support move semantics, I
| had to give up the invariant that any hazard pointer
| instance is properly initialized (I needed to use a null
| pointer to represent the invalid state). That complicated
| all the clients, which now had to either check for or
| assert against the invalid state.
|
| None of these gymnastics would have been necessary in
| Rust. Sure, it doesn't have constructors, but it's easy
| enough to write a factory method that establishes
| constructor invariants, and then you know that any object
| returned from that factory method will satisfy those
| invariants for the _entire lifetime_ of the object. Since
| it is impossible to accidentally use a moved-from object
| (unlike C++), there is no need to introduce an invalid
| state to prevent misuse. I could just freely dereference
| my hazard pointers, with no checks or asserts necessary.
| senderista wrote:
| Here's a much simpler example of something impossible in
| C++ and trivial in Rust: how about a non-nullable
| unique_ptr? The constructor should just be able to check
| for null and then no code need ever check for null again,
| right? Sorry, you need an invalid state to represent a
| moved-from instance, so this is impossible.
|
| Are you telling me that having to accommodate invalid
| states that are semantically both unnecessary and
| undesirable is not a serious limitation of C++ move
| semantics?
| fluoridation wrote:
| Following the previous example, you wrap std::unique_ptr
| in another class that has no move constructor and
| forwards constructor parameters to std::make_unique(),
| and can also be constructed from an std::unique_ptr. Now
| you have a heap-allocated smart pointer class that can't
| possibly be null.
|
| Alternatively, you make it movable, and if someone tries
| to call operator*(), operator->(), or get() on the null
| value, you throw an exception. Not as clean, but, hey,
| it's safe.
| fluoridation wrote:
| It seems like the obvious answer is to have a
| nullable_hazard_ptr and a hazard_ptr, which composites
| nullable_hazard_ptr. nullable_hazard_ptr is movable and
| default-constructible, while hazard_ptr can only be
| constructed with arguments and cannot be moved, but can
| be constructed from a nullable_hazard_ptr &&.
|
| So if you need to return a hazard pointer from a function
| you return a nullable_hazard_ptr and the caller can
| choose to assigned that value to auto or to hazard_ptr.
| In the latter case, the caller will have the guarantee
| that the object is valid because if the function returned
| a null pointer the constructor will have thrown an
| exception. Furthermore the pointer will remain valid
| until it goes out of scope because there's nothing that
| can be done to it to make it invalid (UB
| notwithstanding). Of course, anyone who chooses to use
| nullable_hazard_ptr will need to check for validity.
|
| Unfortunately this does mean that it's the responsibility
| of the callers to choose the right pointer.
|
| >In the constructor of a class with a hazard pointer
| member I needed to be able to initialize a hazard pointer
| on the stack and then move it into the member variable.
| (Because the hazard pointer constructor is fallible, I
| needed to catch exceptions thrown from the hazard
| pointer's constructor and retry from within the
| containing class's constructor, so I couldn't just use an
| initializer list.)
|
| This particular case would be handled by calling a helper
| function in the constructor's initialization list for
| each hazard_ptr member. As I said, this function should
| return nullable_hazard_ptr (always non-null; you will
| have already ensured this inside the function. You still
| need the nullable type because it's the only one that can
| be moved).
|
| Ultimately what you have is something analogous to
| std::lock_guard and std::unique_lock. You are acquiring
| and releasing a resource and in some cases you need to
| tie the acquisition into the program structure and in
| other cases you need to be able untie it. There's no way
| to specify that in C++'s type system other than by having
| two separate types.
| amalcon wrote:
| I've worked a _lot_ with C++, and a small to moderate amount
| with Rust. I tend to prefer Rust when given the choice.
|
| Comparing the overall complexity levels is something of a
| category error, though. Most of the complexity of Rust is in
| the core functionality, idioms, and conventions of the
| language. You'll need to grapple with most of that complexity
| very early.
|
| Most of the complexity of C++ is in the various functionality
| that was either inherited from C or accumulated over the
| decades after that. Most individual pieces of software don't
| use all of that. E.g. approximately nothing will use both
| va_list and variadic templates (ok, maybe indirectly through
| libraries, but not in a way the direct author needs to think
| about). The latter is just a better way of accomplishing what
| the former does. There are lots of variations on this theme.
|
| My sense is that, in practice, Rust has a steeper learning
| curve than C++. I find it more productive now that I'm pretty
| familiar with it, so I think it's worth that steeper learning
| curve. I still think it's a bit steeper.
| armchairhacker wrote:
| Apples to oranges. Rust's borrow system is something you
| couldn't implement in C++, meanwhile C++ has far better
| allocator and compile-time support and probably more features
| in total (things like concepts, intrinsic bitfields, etc.).
|
| Importantly (afaik), Rust has far less features which are
| deprecated and/or in the specification but barely implemented.
| umanwizard wrote:
| I've used both professionally (Rust for the last 4 years) and I
| 100% agree. Rust, even with stuff like async, is a much, much,
| much simpler language than C++.
| lowbloodsugar wrote:
| I've got over a decade in C++ and I won't use it unless forced
| (I.e. paid more money than I can reasonably refuse). It's only
| gotten worse and worse. I would and have used C before I'd use
| C++ and now I'd use Rust before either of them.
| layer8 wrote:
| > When you read above about std::pointer_safety, did you
| understand the difference between relaxed and preferred? If you
| did, please explain in the comments section.
|
| [Since I don't want to sign up with Disqus, I'm commenting here.]
|
| According to https://cplusplus.github.io/LWG/issue1098,
| "pointer_safety::preferred might be returned to indicate to the
| program that a leak detector is running so that the program can
| avoid spurious leak reports".
|
| This means that a program which uses different program logic
| depending on whether a GC is running or not, can choose to opt
| for the non-GC logic when the GC is running with a leak detector.
| fluoridation wrote:
| Who would write two separate but equivalent codebases, one
| which manages memory with RAII and another that assumes a GC,
| just to use that value for _something_? If you 're already not
| assuming a GC is present then why rewrite every a second time
| assuming it's not?
| layer8 wrote:
| This is probably aimed at libraries designed to be compatible
| with both kinds of environments. A library might implement
| some localized alternative logic to maximize efficiency in
| both cases. C++ is all about maximum runtime efficiency while
| allowing libraries to provide comprehensive compile-time
| abstractions.
| fluoridation wrote:
| A library that uses RAII instead of relying on a GC should
| work with or without a GC.
| layer8 wrote:
| RAII requires allocation patterns to coincide with
| lexical scopes, or to use reference counting, which is
| usually more expensive than GC. If RAII were all you
| need, no one would be using GC in the first place.
| deeviant wrote:
| RAII ties resource management to lexical scope but
| doesn't strictly "require" it. Reference counting vs. GC
| cost varies by context--neither is universally "more
| expensive." Both RAII and GC have their merits; their use
| depends on the language and application needs, not on one
| being strictly superior.
| layer8 wrote:
| Right. I could also have said "If GC were all you need,
| no one would be using RAII in the first place". No
| contradiction here.
|
| So it can make sense for a library to support both, to
| accommodate different application needs.
| ori_b wrote:
| How would you write a library to support both without
| being fully RAII correct? And if it's fully RAII correct,
| isn't the GC is a no-op?
| layer8 wrote:
| There are several ways I can think of. For example, a
| library that stores application-provided elements (e.g. a
| graph library where nodes/edges can carry application-
| specific data) can support by-value RAII elements as well
| as by-reference GC'd elements and by-reference library-
| memory-managed elements (using new/delete). As another
| example, the destructor of a library-provided class might
| have to free a complex internal heap-based substructure,
| a potentially expensive operation (having to iterate over
| the substructure) that it can skip (only) when running
| under a GC.
| ori_b wrote:
| That's kinda the point: you can't skip it with RAII,
| unless you avoid exiting a scope (longjmp?).
|
| I suppose you can have two versions of the class, and
| instantiate the leaky one only if you have GC, but that
| seems like work that the GC should free you from. It
| seems better to optimize the memory allocations, perhaps
| with an arena.
| layer8 wrote:
| Not sure what you mean. My point is, for the case of no
| GC, you have to provide a destructor, even if the work
| done by the destructor only consists of freeing memory.
| With GC, calling the destructor could be omitted if it
| only frees memory. Client code however doesn't know what
| a destructor does internally, so always has to call it
| even under a GC. The implementation of the destructor,
| however, can check whether it is running under a GC, and
| can then skip the freeing.
|
| Edit due to your edit: An arena allocator isn't always
| convenient, for example when you have multiple objects
| with different lifetimes that move and/or partially share
| internal elements between them, so the element lifetimes
| are largely orthogonal to the containing object's
| lifetimes.
| PaulDavisThe1st wrote:
| > My point is, for the case of no GC, you have to provide
| a destructor, even if the work done by the destructor
| only consists of freeing memory.
|
| for the case of no GC, a destructor must be provided.
| That's an important difference.
| jlokier wrote:
| You would usually do it for performance rather than RAII
| correctness.
|
| For example, without GC, your library might use RAII with
| std::shared_ptr, for certain objects that outlive lexical
| scope. That is, reference counts tied to pointer copies
| and destructors. With GC, your library might omit the
| reference counts when deferred object destruction is ok,
| saving a little space and time.
|
| If your library is dealing with something like a graph
| (nodes connected by pointers) which may have cycles,
| std::shared_ptr will not be enough to free all objects as
| they become unused, but the environment GC will do it.
| Therefore, without an environment GC, your graph-using
| library will need its own cycle detector, or if usage
| permits, something with arenas or std::weak_ptr. With GC,
| your graph-using library can omit that code entirely.
|
| There are occasions when you'd want to add code for
| correctness when using a GC, to release objects by
| clearing pointers to them in some circumstances where
| it's safe to leave the pointer uncleared (and possibly
| invalid but harmless) without GC.
| rightbyte wrote:
| You can RAII into global scope too though. Unless you
| count that one as lexical.
|
| RAII into a stack and you got dynamic scope.
| tempodox wrote:
| > If you are surprised to learn that the C++ standard had support
| for GC, you are not alone.
|
| I am relieved to learn that I wasn't sleeping too deeply.
|
| > A bit of simplification to the standard never hurts.
|
| Absolutely right, but I'm afraid it might be too little too late.
| I doubt any one person can keep all of C++ inside their head.
| Night_Thastus wrote:
| You don't need to keep it all in your head. That wouldn't be
| practical or useful for most languages. Especially when
| libraries get involved.
|
| I think the better answer is to just remember the bits that are
| frequently useful to you, and pull up a tab on the rest if and
| when you need it.
| SonOfLilit wrote:
| In all other languages I worked in, I basically can keep the
| entire language (not stdlib) in my head, and it's extremely
| valuable to understand what's going on. In C++ I just can't.
| kllrnohj wrote:
| Without looking it up do you know what are the Java memory
| model guarantees? Or what's the difference between >> and
| >>> ?
|
| Even "simple" languages have things that _most_ people
| simply don 't remember without looking it up because it's
| just not useful to know in day to day work.
| SonOfLilit wrote:
| Thankfully I've never worked seriously in Java. Ask
| similar questions about Python, C, x86 assembly, old
| versions of C#...
|
| I just looked up >>>, it's something I'd definitely know
| had I delivered any Java projects.
| Night_Thastus wrote:
| I mean, listing x86 assembly is a joke, right?
|
| Everyone knows the simple add/mov/etc commands, but the
| list of total commands is in the multiple thousands, many
| with very unintuitive effects.
|
| I don't think there's a person on earth who knows _all_
| the X86 language.
| t-3 wrote:
| It's actually not _that_ bad. There are a bunch of
| obscure things, but most of the mnemonics are just
| variations with slightly different names.
| LastTrain wrote:
| I am catching a whiff of judgement in your reply. You are
| the person on the team who knows the ins and outs of the
| language and is proud of it. You are a valuable member of
| the team, but I don't want a whole team of you. There are
| other skills that are just as important in getting
| something "delivered" as knowing all the syntax.
| wrs wrote:
| Judgment? Like assuming that if someone understands the
| language they're using they must not have other skills?
| :)
| LastTrain wrote:
| I said there are other skills that are just as valuable
| that I look for when building a team. Exhaustive
| knowledge of a language is a perfect skill for writing
| docs, training, performing interviews & doing code
| reviews. My main point was, it is not a required skill to
| be a "good" developer, like you seemed to imply.
| pjmlp wrote:
| List all features supported in C# 12 and on which runtime
| version were made available, without looking into the
| language reference.
|
| Bonus points for the ones that have different behaviour
| between .NET Framework and Core.
|
| Extra bonus points when taking Mono, .NET Native, IL2CPP
| into consideration.
| cogman10 wrote:
| Yes... but I think the real question that will stump
| people is "What is <? super Foo> and how is it different
| from <? extends Foo>". :D
|
| Java generic semantics are wild and often surprising.
|
| But too your point, I think typescript is FAR more
| complex than C++ yet I almost never see similar
| complaints of its complexity. The richer the type system,
| the more complex the language.
| jcranmer wrote:
| That's not the real confusing one in generics. The real
| confusing one is "What is the difference between
| Class<List> and Class<List<?>>" (i.e., mixed generics and
| raw types)... I only know about this because it bit me in
| the ass once!
| cogman10 wrote:
| Ha! I thought about doing a tricky thing with wildcards
| vs missing generics.
|
| What gets even more wild is, for reasons I really don't
| understand, when a missing generic makes its way into a
| generic pipeline it seems to have the tendency to erase
| other generics. (IE, `list.stream().map((f)->new Map())`
| does weird things with the stream as it gets more
| complex.). You can throw in "What's the difference
| between List<Object>, List<?>, List, and List<? extends
| Object>" for good measure :D
| charcircuit wrote:
| >I think the real question that will stump people is
| "What is <? super Foo> and how is it different from <?
| extends Foo>"
|
| No, I don't think so. These are both common to see
| especially since Java 8 added Consumer as the former used
| to be pretty rare to see.
| tsimionescu wrote:
| These are both pretty intuitive - you can provide any
| class which is extended by or extends T respectively.
| Some of the consequences are less intuitive, but I don't
| think it's any hard to remember. As someone who last
| professionally programmed Java during Java 7 days, it was
| still clear to me what these mean.
| jcranmer wrote:
| > Without looking it up do you know what are the Java
| memory model guarantees?
|
| * Java is a happens-before language--you need a sequence
| of X happens-before Y from a write to a read that crosses
| threads for the language to be well-defined. I'm not
| going to list the "obvious" cases of happens-before
| relations.
|
| * volatile variables in Java are sequentially consistent
| atomic variables.
|
| * 64-bit loads are atomic in the sense that word tearing
| cannot be observed.
|
| * Writes to a final variable in an object's constructor
| happen-before the end of constructing an object.
| (Probably the easiest thing for people to forget)
|
| * There's a bunch of complex rules to try to explain what
| happens if you violate happens-before and you have a data
| race. These aren't really right anyways, so you can
| safely ignore these and pretend it's UB if you do create
| a data race, which is what happens in other happens-
| before language models.
|
| > Or what's the difference between >> and >>> ?
|
| The first is arithmetic shift right and the second is
| logical sign right; alternatively, the first will copy
| the sign bit into the lower vacated bits while the latter
| does not.
| tialaramex wrote:
| > you can safely ignore these and pretend it's UB if you
| do create a data race
|
| I mean, that's safe but it's thoroughly unnecessary. In a
| language where races are UB all bets are off and that's a
| pretty wild consequence for what may be a relatively
| minor mistake.
|
| Because data races aren't UB in Java, our program even
| though its behaviour now likely defies ordinary
| understanding by its creators, does still have some
| coherent behaviour. For example maybe we raced code
| that's putting CustomerOrders in a List, and so the List
| is smashed. In a language like C++ it's entirely possible
| that the smashed List is so toxic that even asking how
| big it is potentially has completely insane answers,
| crashes, runs arbitrary code, anything might happen - let
| alone if trying to take all the CustomerOrders out one at
| a time for processing. In Java, maybe the List is empty,
| which is not what we wanted, or it has all the orders
| which shouldn't be there, it won't have for example a
| negative number of CustomerOrders or anything else
| completely nonsensical.
|
| In terms of defensible business logic, Java's memory
| model isn't better. But in terms of "Do attackers who
| exploited my race also get to run arbitrary machine code
| with database access?" the answer is definitely going to
| be "No" in Java for a data race, unlike in C++ or even
| Go.
|
| Also, while it's true that Undefined Behaviour is common
| for data races (because of SC/DRF and laziness), it's not
| what happens in OCaml, they've come up with an even
| cleverer scheme than Java so that they hope their
| programs remain not only strictly well defined, but maybe
| something you can reason about despite a data race.
| adhesive_wombat wrote:
| I just have Zeal bound to a hotkey. cppreference is never
| more than 250ms away!
| skywal_l wrote:
| The problem with this is what you don't know can hurt you,
| that's why I like Sean Baxter initiative[0], where you can
| selectively disable c++ features (at file level!).
|
| [0] https://www.circle-lang.org/
| CoastalCoder wrote:
| I was about to say the same thing. With C++, you don't
| necessarily know if you're looking at code that uses
| unfamiliar language rules, especially when templates are
| involved.
| kstrauser wrote:
| I think I have about 95% of Python in my head. I occasionally
| see a new edge case I hadn't considered, but it's been a
| while. The language itself is rather compact. There aren't so
| very many keywords or builtin functions. Even the stdlib is
| manageable, although I'm always pleasantly surprised when a
| module has added some new functionality or convenience that
| will save me time. It's knowable.
| hota_mazi wrote:
| The problem with Python (and dynamically typed languages in
| general) is not so much knowing the language, but
| understanding the code that people write with it.
|
| Since there are no type annotations to help your
| understanding of the code, you need to keep a lot in your
| head, as opposed to statically typed languages.
| kstrauser wrote:
| That's not been my experience at all, but I could see it
| being confusing at first.
|
| Also, we do have type annotations now. They're not
| universally used, and definitely not required, but mypy
| and friends can go a long way toward finding type issues
| even without them.
| pjmlp wrote:
| The almost 3000 pages of language reference, standard
| library and C extension API, and changes across language
| versions?
| kstrauser wrote:
| Yeah, because there's a whole lot of redundancy. If you
| "know" 3.11, and upgrade to 3.12, there are only going to
| be a few changelog items you'll actually care about. The
| rest are generally internal implementation changes that
| don't affect how you use the language.
|
| OK, so I don't have every corner of the stdlib memorized,
| especially the odd deprecated batteries-included stuff
| like playing sounds on a Sun workstation. Nor do I have
| the extension API docs committed to memory, as you can
| use the language a lot without having to write those
| things yourself.
|
| But the core of Python, like the language itself and the
| most common parts of the stdlib that are actually used in
| 95% of Python code, I have a strong grip on. In
| particular, the actual language is pretty simple.
| abound wrote:
| Other people have touched on this, but I think one's ability
| to be productive in a language is highly correlated with how
| much of the language (and project domain) they can "keep in
| their head" at a time.
|
| This is why Go is my (and seemingly a lot of other people's)
| go-to "get shit done" language, I can write and edit code for
| hours without having to think about (too many) footguns or
| hunt for docs and esoterica.
| adrianN wrote:
| I can write and edit C++ for hours without thinking too
| much too, and I'm no language lawyer. Almost all code one
| encounters in the wild is not particularly esoteric.
| jrmg wrote:
| Being able to have it all in my head was something I loved
| about Objective-C (alas...)
| cogman10 wrote:
| > but I think one's ability to be productive in a language
| is highly correlated with how much of the language (and
| project domain) they can "keep in their head" at a time.
|
| I disagree. I'd say productivity has very little to do with
| language familiarity and way more to do with ecosystem
| familiarity. C++ is hard because the ecosystem has no real
| entry point and several "script language to write a script
| language to write a compilation definition to write a
| script language to compile a project" build systems. (Who's
| familiar with M4?)
| hota_mazi wrote:
| That's fine if you're the only one working on a project but
| it doesn't work as soon as there are more than one person
| contributing to the code base.
| Night_Thastus wrote:
| As someone in a large, long-tailed codebase with multiple
| people working on it...it really hasn't been an issue. As
| long as people know the basics, and leave comments for
| anything weird they're doing, it's fine.
|
| The only time I've gotten friction is when in another
| project someone was _really_ eager about making everything
| as abstract and modern as possible, to the point the code
| was (in my eyes) nearly unreadable. But that 's not a C++
| problem, that's a dev problem.
| travisgriggs wrote:
| I can see this as an attitude to stay the course
| using/leaning it. But not as an end game; "you'll never get
| it, don't even try!" tThe idea that you as a team might
| develop a large piece of software, but have an unknown feel
| for how much more there is that is unknown, is scary to me.
|
| And a bit ironic. I lived through the static compile
| type/dynamic late type wars. There was this argument that
| with C++ (and similar) you knew and had some compiler
| ratified certitude you could count on. But if you're telling
| me that a junior/intermediate dev can write code, perhaps
| making head scratching design concessions to get the thing to
| compile, he might make code that appears to run in the common
| case, but has unexpected behavior in other as of yet
| demonstrated areas, because they can never hope to have
| enough knowledge of the language to build a semi accurate
| model to know what to expect, then we're really no better off
| than those late bound interpreted hippies with their weird
| metaprogramming VMs.
| Night_Thastus wrote:
| Whoah, I think you completely mis-read my intention.
|
| My point is that C++ offers users a _lot_ of tools. You don
| 't need to use most of them to solve _most_ problems. Just
| start with simple tools: functions, classes, standard
| templates, etc.
|
| That 10% effort will solve 90% of problems.
|
| If you find a problem that can't be solved with the simple
| tools, then start looking into more advanced features C++
| offers and use them if appropriate.
|
| _That_ is why no-one needs (or should try) to keep all of
| C++ in their head. It 's not necessary for most problem
| solving.
|
| It's of course fine to be enthusiastic about it and learn
| some new things just for the sake of learning. But it's not
| a _requirement_ to know all of C++ to _write_ C++.
| thesuperbigfrog wrote:
| >> But it's not a requirement to know all of C++ to write
| C++.
|
| You just need to know as much as everyone else on your
| team and all of the libraries that you are using.
|
| "Within C++, there is a much smaller and cleaner language
| struggling to get out". Yes, that quote can be found on
| page 207 of The Design and Evolution of C++. And no, that
| smaller and cleaner language is not Java or C#. The quote
| occurs in a section entitled "Beyond Files and Syntax". I
| was pointing out that the C++ semantics is much cleaner
| than its syntax. I was thinking of programming styles,
| libraries and programming environments that emphasized
| the cleaner and more effective practices over archaic
| uses focused on the low-level aspects of C." Source: http
| s://www.stroustrup.com/quotes.html#:~:text=%22Within%20C.
| ...
|
| The question is then, "which 'cleaner and more effective
| practices' are being used in the code you write and
| maintain?"
| Night_Thastus wrote:
| You only need to know the parts you're modifying. Not
| everything at once.
| tempodox wrote:
| Only someone who knows too little about C++ would say
| something like that. Different features can interact in
| subtle and unexpected ways and that can bite you at
| runtime without any warning at compile time.
| Night_Thastus wrote:
| I think that's too broad of a generalization, and I'm not
| following what kind of interaction you're talking about.
|
| The most obvious is if someone included a library that
| was dumb enough to be compiled with -ffastmath but the
| days of that are mostly long-gone.
| benj111 wrote:
| A criticism of lisps and forths is that users offer end up
| writing their own languages that no one else can understand.
|
| If everyone is using a different niche of the same language
| then you have the same problem.
| Symmetry wrote:
| C++ is the language that I know the most things about, but I
| would never say it's the language I know the best because there
| are _so many_ things to know about it.
| pradn wrote:
| It does make it a somewhat intellectual exercise, precisely
| because it is so complex. A few years of C# and I felt like I
| knew the whole language quite well. Double the time with C++
| and I'm fluent, but no where near an expert.
|
| So that's one way to not be bored and to challenge yourself
| at work every day. :)
| tambourine_man wrote:
| Thank you, not a C++ at all, but I thought I knew better.
| westcoast49 wrote:
| Sean Baxter probably got close when he created Circle C++.
| Other than that, I think you're right.
| pjmlp wrote:
| It never suited the use cases for Unreal C++, C++/CLI, and
| COM/WinRT, with the three major C++ implementations of automatic
| memory management ignoring it, no wonder it was standard dead
| weight and should be removed.
|
| That is what happens when features are added without taking into
| consideration existing use cases.
| dgellow wrote:
| Do you know the history of its inception? I feel that would be
| a good read. Like, what use cases did the original authors had
| in mind, how did they convince others to accept a GC in C++
| spec, etc.
|
| I like these kind of development stories, it's often a weird
| mix of social and technical challenges, from a specific time
| period.
| jcranmer wrote:
| As far as I can tell, the initial paper proposal is
| https://www.open-
| std.org/jtc1/sc22/wg21/docs/papers/2007/n23..., proposed for
| C++0x.
|
| The subsequent history appears to be this:
|
| * The "Kona compromise" of 2007, where a few features were
| stripped down to try to be able to get a new version of C++
| shipped by the end of 2008 (which turned out to slip to 2011
| anyways). This meant that GC went from a full-bore proposal
| to a barebones proposal.
|
| * https://www.open-
| std.org/jtc1/sc22/wg21/docs/papers/2007/n24... is the first
| proposal of barebones GC support. As you can see, it's
| stripped down a lot from the original GC proposal. What
| remains is largely "it's UB to have a pointer not be visible
| as a pointer address, except if you use new library
| functionality to declare your pointer shenanigans."
|
| * https://www.open-
| std.org/jtc1/sc22/wg21/docs/papers/2008/n26... is the final
| variant that was voted in for the June 2008 meeting (see
| https://www.open-
| std.org/jtc1/sc22/wg21/docs/papers/2008/n26...) with no
| opposition (although some abstention).
|
| * https://www.open-
| std.org/jtc1/sc22/wg21/docs/papers/2020/p21... proposes, in
| 2020, to remove the features added by N2670 on the basis of
| no real implementation or real-world support. The discussion
| in EWG indicates favor for removal (only one person
| objected), while LEWG indicates even stronger favor (nobody
| objects).
| dgellow wrote:
| Wow, that's a lot of context. Thank you very much!
| WhereIsTheTruth wrote:
| Unity/Unreal, devs who work with both engines complain about
| the GC, it never was something that made them pick the engine
| specifically
|
| When you dig into projects, you realize that most of the time
| people do extra work to work around the GC (pooling, arena
| allocation)
|
| GC is nice to have but is _very_ task specific
|
| Anyone who sells a GC as a universal solution is clueless and
| is dangerous
|
| At 120fps, your frame budget is only 8ms, you don't have time
| to waste on GC related tasks, in fact, you don't want to do any
| kind of heap allocation or anything that might introduce any
| form of hiccups during gameplay (no JIT as well)
| pjmlp wrote:
| Likewise people that usually use the GC as escape goat for
| not writing proper code in first place, like using the
| language features for allocation free code.
|
| Those are the same folks that do memory allocation in C
| during frame rendering, with the compiler provided
| malloc()/free() lousy for multithreaded game code, while
| complaining about the GC.
|
| Summary, complaining only allowed to those that actually
| master the toolbox.
| moralestapia wrote:
| I was once made fun of (in a kind of humiliating way, now that I
| recall) by a bunch of HPC scientists in a room at KAUST for
| saying that C++ had support for GC.
|
| They were like _" if you knew the least bit a about C++, you'd
| knew it doesn't have a GC"_.
|
| I was like, "yeah cool, but C++ _does have_ some GC functionality
| ", but they just shut me down. My background is Biology so there
| were a few remarks about how "a biologist just doesn't know these
| things, lol".
|
| This was around 2019, btw. And sure, I'm a biologist, but I also
| happen to have spent 8-10 hours/week for the past 20 years coding
| because I just really like it; so, there's my 10k hours,
| -\\_(tsu)_/-.
| foota wrote:
| This is why I always carry around a hardcopy of the C++ spec so
| I can pull it out Phoenix Wright: Ace Attorney style /s
| MR4D wrote:
| That must be heavy! LOL!!
|
| For CHF 208, you can buy the reference directly from
| ISO...all 1,853 pages! [0]
|
| [0] - https://www.iso.org/standard/79358.html
| foota wrote:
| If anyone tells you C++ isn't a safe language, watch them
| try to stop a bullet with the C# language spec!
| rzzzt wrote:
| ECMA-334 (the C# language portions): https://www.ecma-
| international.org/publications-and-standard..., 639 pages
|
| ECMA-335 (the Common Language Infrastructure portions):
| https://www.ecma-international.org/publications-and-
| standard..., 574 pages
| CoastalCoder wrote:
| Ok, but if I need to be saved from an _anti-material_
| round, I 'm sticking with C++.
|
| Just sayin'
| foota wrote:
| I admit I had to shop around for a smaller language spec
| and chose C# :)
| pjmlp wrote:
| Note that those 639 pages are only for C# 6, for further
| pages the Word documents provided with the SDK are
| required.
| rzzzt wrote:
| Let's equip the addenda. It should enable us to withstand
| short bursts from a phased plasma rifle in the 40 watt
| range as well.
| Tomte wrote:
| At some point you could get it for 18 USD from ANSI, and
| later for about 30 Euros from the Estonian standardization
| office.
|
| Both offers had their prices, erm, fixed.
| josefx wrote:
| You joke about that, but people who blindly "know things"
| will refuse to even look at the standard. After all they
| already know what it says on the topic. So even carrying the
| standard around with you wouldn't help.
| CoastalCoder wrote:
| Probably safe to say that _everyone_ has issues.
| gumby wrote:
| I've never successfully submitted a PR for someone's
| issues. Someday...
| tialaramex wrote:
| You'd have to print out your own, you can't actually buy a
| hard copy of any recent ISO C++.
|
| Some national standards bodies, entitled to do so, might
| claim they can sell you one when actually they've got a deal
| with a relatively local print-on-demand company, but because
| this standard is huge that company will reject the order so
| you in fact can't buy the product you'll get an apology, or a
| PDF or both despite specifically buying paper.
|
| Although their processes are hopelessly antiquated (fly to
| Hawaii to fix a typo? I don't think so), the result is, as
| you might expect in 2023 - just a bunch of document layout
| source, from which "draft" versions of the document are
| publicly available.
|
| Not as useful if you're getting burgled, or to raise a
| display off the desk, but more practical for real work.
| aidenn0 wrote:
| I find Stroustrup's book to be more accessible; the C++ spec
| can get rather math-y for some people.
| ComputerGuru wrote:
| So far as I can see, this was in the C++ _spec_ but not
| implemented by any major compiler, though. So I guess the
| technical answer to "who's right?" would depend on the verbiage
| of the discussion. :shrug:
|
| (But regardless, that sounds like a toxic environment and I'm
| glad you're out of there. To think that because someone has a
| degree in biology, that automatically precludes them from
| knowing anything about computers or programming that you don't
| is just the epitome of arrogance and hubris.)
| slaymaker1907 wrote:
| I'm kind of surprised they didn't try to get rid of/adapt the
| standard for C++/CLI on Windows since it actually uses GC for
| a lot of things. However, I guess the standard wasn't all
| that useful, especially since it mandated a lot of overhead
| for pointer arithmetic. My theory is that it is very
| difficult to come up with a general API for interacting with
| GC'd memory in C++ because making the API efficient depends
| heavily on how it is implemented.
| bjourne wrote:
| If it came to that it's time to whip out the technically
| right big dick and remind them that C++ has had smart
| pointers since forever and that TRACING garbage collection is
| far from the only automatic garbage collection technique
| known to man. ;)
| astrange wrote:
| C++ has had conservative tracing garbage collection since
| forever; GCC is garbage collected and you can use Boehm gc.
| influx wrote:
| Some of the best programmers I've worked with have
| backgrounds in sciences, and it's been a delight just being
| around them as they apply some of that knowledge in in a
| different field.
| vore wrote:
| I would probably agree with those scientists, this seems like a
| "well actually" kind of gotcha claim with no practical
| significance when there is no usable implementation of this.
| CoastalCoder wrote:
| I think the main issue with those folks wasn't if/how they're
| right, but rather how they treated the commenter.
| moralestapia wrote:
| This.
|
| The thing was so obscure that even seasoned people who have
| worked with this for many years didn't know about it.
|
| I guess the takeaway is to stay humble because you never
| know what you don't know.
| pavlov wrote:
| And more specifically, never get into arguments about
| what C++ can do because it's guaranteed that someone has
| used it the way you assume it should never be used.
| astrange wrote:
| There is one.
|
| https://hboehm.info/gc/
| slingnow wrote:
| What does this have to do with the article? This sounds like
| you're here gloating about some pedantic argument you made 4
| years ago. And of course we are only hearing your side, so of
| course you're the hero.
|
| And judging by the fact that this is currently the top comment,
| I guess this is what other people come to HN (now) to read in
| the comments. Color me confused.
| quietbritishjim wrote:
| Newer comments tend to float near the top for a while to give
| them a chance to get some upvotes.
| moralestapia wrote:
| >What does this have to do with the article?
|
| Did you read it? It's about GC in C++ being a relative
| obscure feature that is now going to be removed.
|
| >some pedantic argument you made 4 years ago
|
| I just shared an anecdote related to it and I thought it'd be
| appropriate bc. this is/was quite likely the _only_ chance I
| 'd have in life to talk about this very very very specific
| situation with people that would understand it. And yes, it's
| getting a few upvotes, weird thing to be mad at that but to
| each its own.
| skitter wrote:
| It was in the spec but never implemented, so it's obly a
| feature of C++ if you take the stance that there are no
| real implementations of C++.
| moralestapia wrote:
| IIRC it was present in Clang back at the time (which was
| relevant to us as we were using it); not sure about gcc
| or others.
| TeMPOraL wrote:
| If you want to go this way, then take a look at compiler
| support matrix for last few language versions - if having
| an implementation is necessary for something in spec to
| "be a feature of C++", then C++20 and C++23 are still
| missing some features they're supposed to have.
| pjmlp wrote:
| Several features in ISO are yet to be fully implemented,
| there are compilers still catching up with C++17.
|
| GC isn't the first one to victim of such lack of
| implementations, only EDG implemented C++98 external
| templates, for example.
| pbsd wrote:
| extern templates are a C++11 feature. export templates
| are the removed C++98 feature.
| pjmlp wrote:
| Thanks for the correction, I meant obviously export
| templates.
| Kranar wrote:
| We should be clear, GC was never a feature in C++.
|
| What these features do, which are being removed, is make it
| easier to implement a GC allocator in a standard conforming
| and portable fashion. Current GCs in C++, such as Boehm's
| GC, depend on platform specific hacks that are incredibly
| brittle.
|
| As it turns out, these features do not make it easier to
| actually implement a GC allocator in C++, so they are being
| removed.
|
| I think most people can differentiate between a language
| providing a feature as a first class native property, and a
| library or feature built on top of that language. Most
| people would not argue against the position that C++ does
| not contain a web browser as a feature, even though it's
| possible to build a web browser using C++.
|
| Similarly, C++ does not and has never had garbage
| collection, but for a time the standard did provide
| facilities that was supposed to make it easier to implement
| a garbage collection library.
| jpcfl wrote:
| Classic gatekeeping.
| sidewndr46 wrote:
| It's not just you and not just biologists. I've had engineers
| with 20+ years of experience tell me that fixed width types
| don't exist in C++
| clnq wrote:
| Presumably 20+ years of experience somewhere far away from
| C++.
| bfrog wrote:
| Doubtful, C++ tends to bring exactly this sort of persona
| to the forefront
| sidewndr46 wrote:
| no the majority of the career experience was with C++
| Kranar wrote:
| Depends what they meant. An expert in C++ likely meant that
| C++ does not guarantee any fixed width types. Whether they
| are provided depends on the implementation.
| kazinator wrote:
| You have to arm yourself with facts.
|
| - know exactly what you mean by "C++ has support for garbage
| collection"
|
| - have the chapter-and-verse citations from ISO C++ ready if
| someone disagrees
|
| - understand the limitations of what is required in the
| document, and be the first to point that out
|
| - and have a good idea of some of the landscape in terms of
| what of those requirements is implemented in the wild
| EmilyHughes wrote:
| How about you stop caring that much about proving others
| right.
| SillyUsername wrote:
| He cares about proving others wrong...
| timacles wrote:
| Thats just a good approach to being well versed on a topic,
| it doesn't have to be for adversarial situations only
| bfrog wrote:
| That goes against the entire premise of C++ developers
| mindset.
| kazinator wrote:
| You mean proving others wrong?
|
| In this situation, those others were not exactly wrong. It
| was about OP proving also not being wrong.
|
| If people in your organization think you said something
| stupid, that could be bad for your opportunities there.
|
| If you say something which is correct, but the correctness
| depends on some unusual conditions that are not well known,
| or special interpretations of common concepts, or some
| context that is not shared with whoever you're talking to,
| you have to either not mention that, or spell out those
| details.
|
| If you posit that pigs do fly, don't forget the detail that
| it's when they are shot out of a cannon.
| coolsunglasses wrote:
| tbqh kazinator is manifesting the bare minimum of Caring to
| be trusted with C++ code as a developer. If you aren't
| prepared to get into bizarre pseudo-legal arguments with
| collaborators about language specifications you are going
| to be happier somewhere else in the software industry.
|
| I do not say this as someone who believes this represents
| healthy behavior or that wants to write C++ for a living.
| They've been like this since #C on EFNet and FreeNode were
| a thing 30+/25+ years ago. My guess is they got the
| behavior from Usenet or something but I didn't hang out
| there so I don't know first-hand.
| BeetleB wrote:
| He was fighting social proof. Because they viewed him as an
| outsider no amount of facts will convince them. They won't
| take his word for it and they won't spend the time verifying
| his facts.
|
| Been there many times. Including when a room full of
| programmers laughed at me for suggesting randomizing a list
| prior to using quick sort because "it messes up the big Oh
| complexity"
|
| (Yes yes I know there are simpler ways to handle quick sort
| attacks but that wasn't why they were laughing)
| m463 wrote:
| lol, we all want those things in the moment.
|
| There are so many arguments that I could have won, if I had
| just had an evening to mull it over.
|
| I'm reminded of the movie Annie Hall where Woody Allen is
| waiting in line... Alvy: [Hearing a man
| behind him rambling about Marshall McLuhan]
| What I wouldn't give for a large sock with horse manure in
| it. [Turns to the camera] What do you do when
| you get stuck in a movie line with a guy like
| this behind you? It's just...maddening- Man in
| Theatre Line: [Notices Alvy and walks up to him] Wait a
| minute, why can't I give my opinion? It's a
| free country! Alvy: Did-did he, he can give you- Do
| you have give it so loud? I mean, aren't you
| ashamed to pontificate like that? And the funny
| part of it is, Marshall McLuhan; you don't know anything
| about Marshall McLuhan! Man in Theatre Line: Oh
| really, really? I happen to teach a class at
| Columbia called "TV, Media, and Culture." So I think that my
| insights into Mr. McLuhan, well, have a great deal of
| validity! Alvy: Oh, do ya? Well, that's funny,
| because I happen to have Mr. McLuhan right
| here, so, so, yeah, just lemme lemme lemme -- [pulls McLuhan
| from behind a nearby poster stand] -- Come over here for a
| second. Tell him! Marshall McLuhan: I
| heard what you were saying. You know nothing of my
| work. You mean my whole fallacy is wrong. How you ever got to
| teach a course in anything is totally amazing. Alvy:
| [To the camera] Boy, if life were only like this!
| SillyUsername wrote:
| It's like when you say Java has a GOTO. It's in bytecode, in
| the language as a reserved word, and has a rarely used partial
| implementation vis-a-vis label:
| https://www.geeksforgeeks.org/g-fact-64/ Ultimately,
| thankfully, it never got made into a real keyword.
| tester756 wrote:
| >Ultimately, thankfully, it never got made into a real
| keyword.
|
| Why "thankfully"?
| ska wrote:
| canonically: https://homepages.cwi.nl/~storm/teaching/reade
| r/Dijkstra68.p...
|
| but see also 50+ years of back and forth on that subject,
| other opinions available.
| jsight wrote:
| It is shocking how groups can create an environment of being
| proudly wrong. Once you see the pattern, you see it everywhere.
|
| Hahaha, how can that guy be so stupid as to think the world
| isn't flat? Easy to see in hindsight, but it happens with
| everything (programming language a vs b, OS A vs B, etc).
|
| Groupthink is strong and even the people who agree with you
| might find themselves not pointing it out.
| harry8 wrote:
| That kind of blatant ass-holery is really common in C++ circles
| and you even see Bjarne doing it.
|
| "I understand what you are trying to say and I think you are
| wrong" at a conf to delighted razzes and jeers from the room.
| Bjarne had seemingly just discovered that link lists thrash
| cache and was presenting about swapping in a vector instead.
| Note it's got nothing to do with whether this guy is onto
| something or not. It is possible to disagree without being an
| asshole. Bjarne missed the opportunity. C++ culture follows.
|
| https://youtu.be/SfkMiGFVhZo?si=pd36kbm9FFoKFuKm&t=4111
|
| IMHO it is unsurprising there's a lot of groupthink involved in
| such a culture and it has really damaged the language and
| certainly makes it unpleasant in the way you describe.
| zerr wrote:
| At least `export` keyword was implemented in Comeau C++ :)
| SillyUsername wrote:
| Seems strange to me them doing this rather than implementing at
| least partially something like an optional flag for memory
| tracing safety features instead. A bit like we're C, we love
| pointers and overflows, we don't need no stinking GC or safety
| rails :D
| bfgeek wrote:
| One interesting thing which folks might not be aware of - most
| objects in Blink (Chromium rendering engine) are GC'd:
| https://v8.dev/blog/high-performance-cpp-gc
|
| This is in part due to lots of objects having strong references
| from JS objects, and the complexity in dealing with already
| having a GC with reference counting techniques.
|
| Additionally its much harder to introduce UAFs & Type confusion
| bugs, but also has interesting performance implications (not as
| simple as GC is slow - a lot of the time GC'ing objects can make
| things faster).
___________________________________________________________________
(page generated 2023-11-01 23:01 UTC)