[HN Gopher] Is C++ Doomed?
___________________________________________________________________
Is C++ Doomed?
Author : ingve
Score : 62 points
Date : 2022-02-23 20:32 UTC (2 hours ago)
(HTM) web link (tednesday.wordpress.com)
(TXT) w3m dump (tednesday.wordpress.com)
| LoveGracePeace wrote:
| Simple, it's a war between those who are Pascal inclined (and
| probably liked Macs and Steve Jobs) e.g.
| (https://www.quora.com/Why-does-the-type-go-after-the-variabl...
| - add Rust, Kotlin and Scala) and those who know the best
| languages, the ones that are actually used in production at
| companies large and small worldwide: C, C++, Java and C#. IOW, if
| the language does "type varname" then it's correct, if it does
| "varname: type" then it is doomed. It's sarcasm but it's true.
| pubby wrote:
| I agree that C++ is a mess of a design, but I don't think this
| article quite gets how. RAII and move semantics are two of C++'s
| best features, and the ones that any replacement - like Rust -
| will copy.
|
| To me, bigger flaws with C++ are that it's too big. The language
| is bloated with multiple features that accomplish similar things,
| but each with their own odd corner cases and interference.
| Anytime there's an issue from a language component, the solution
| is to tack another language component on top of things. See for
| example the progression of Macros -> Classes -> Templates ->
| Constexpr -> Concepts. Each tried to fix flaws in the previous
| design, but each introduction made the language more complicated,
| not less.
|
| > But exceptions come at a performance cost.
|
| I mean it's slow to throw, but non-throwing code paths are hardly
| affected. Typical implementations store the exception unwinding
| stuff off to the side, so the only cost is binary size.
|
| > That means you need to write code that wraps every possible
| resource in RAII logic.
|
| Consider using scope guards. You can write them directly in your
| code, without a separate class per resource.
| int* ptr = malloc(...); auto guard = defer([&]{
| free(ptr); });
| mFixman wrote:
| From a post in /r/cpp [1],
|
| > As of C++20, the right way to write a trivial getter in C++
| looks like `[[nodiscard]] constexpr auto GetFoo() const
| noexcept -> Foo { return foo_; }`
|
| C++>11 is such a mess than most useful changes are invisible to
| most users due to bloat.
|
| I think it's way past time to call it quits and abandon active
| development of the language. New features will come from other
| languages without 35 years of baggage.
|
| [1]
| https://www.reddit.com/r/cpp/comments/swv6wh/when_not_to_use...
| UncleMeat wrote:
| I'm not sure it is remotely reasonable to choose a language
| for a project based on the verbosity of accessor signatures
| when being maximally pedantic.
|
| C++ is indeed a mess but there is almost no alternative for
| existing enormous codebases than to keep making it better.
| The industry will slowly adopt memory safe system languages,
| but that's going to take ages to go beyond greenfields and
| absolutely security-critical systems.
| Rusky wrote:
| I do think the article's criticism of C++'s move semantics is
| fair. Rust's "copy" of both RAII and move semantics is
| drastically simpler, and C++'s version is part of what makes
| the language so huge.
| Animats wrote:
| The basic problem with C++ is that it has has hiding without
| safety. C has neither, and most newer languages have both.
| Attempts to add safety to C++ via templates always seem to leak
| raw pointers, since many APIs never converted to C++. It's
| significant that there isn't a C++ Linux kernel API, where you
| use C++ strings for everything and get rid of the null-terminated
| stuff.
|
| You see this in the original poster's example. He's doing C-type
| I/O in C++. Of course it doesn't work cleanly.
|
| It's frustrating. C++ is object-oriented. The cool kids don't
| like that. So, Rust isn't object-oriented; it uses traits and
| generics. Those are different enough that there is no one-to-one
| way to convert modern C++ into safe Rust. It takes a redesign.
|
| So we're kind of stuck.
|
| If you're familiar with game engines, consider what it would take
| to convert Unreal Engine to Rust.
| dathinab wrote:
| I would say many (not all) game engines could probably be
| converted, by finding common pattern in usage of inheritance
| and mapping it to rust patterns.
|
| EDIT: Through automatizing this, or making C++ to rust bindings
| for it would be hard.
|
| I mean creating inheritance graphs for entities is as far as I
| know already seen as an anti pattern since a while.
|
| Things like ECM are nicely re-presentable in rust.
|
| Many patterns of inheritance for code re-use tent to also not
| be too hard to represent in rust.
|
| But I would argue that even if rust had inheritance engines
| like Unreal Engine would not be ported, at least not until they
| do a major rewrite anyway. Because of it being a lot of work
| with not too much intermediate gains.
| biesnecker wrote:
| There's a pretty wide gap, though, between "nicely
| representable in rust" and "mechanically translatable from
| the C++ implementation" that mostly requires human effort to
| span.
| dathinab wrote:
| Yes, though even if rust had inheritance I don't think you
| would have a machine translatable mapping good enough to
| port a game engine.
|
| Generally for any language which in design isn't "somewhat
| close to C++" I don't think this would work.
| sto_hristo wrote:
| When i do my periodic refresh runs c++, i often question myself -
| why i'm bothering with it? C++ seems more about programming c++
| itself than programming against the actual problem. At which
| point i reason that if i ever need to go native and fast, i'd
| simply use C, and for the higher level use not-c++.
| uoaei wrote:
| Gonna be a long while before safety and certification bodies for
| industries like aerospace move away from C++. All the tools for
| the past few decades focus on C++ as the main systems language in
| these cases.
|
| Rust is great as a spiritual successor with more stable
| performance (in general) but the institutional inertia with C++
| is strong.
| nine_k wrote:
| Aerospace industry? C++? Frightening.
|
| I thought Ada is prevalent in this space.
| ghosty141 wrote:
| I'm not in the industry but from what I know it's a split
| between Ada/C/C++. More space related applications tend to be
| C/C++ (satelites, rovers, etc.) while the aero-side uses more
| Ada (Boeing, Airbus etc.).
| Jtsummers wrote:
| Ada never really "won". It was mandated by DOD, but the
| industry more or less rebelled at the idea. Fortran was
| pretty common, but C and C++ are the dominant languages. The
| last greenfield aerospace (avionics) project I was on, we
| were given a choice and it seemed that we (and everyone else)
| went with C or C++. There are a lot of static analysis tools
| now that cover most of what you get from Ada (and then some,
| even with SPARK), but the choice was really based on
| familiarity for potential new hires. You also use strict
| subsets of C or C++ in this field, not every feature will be
| available. (I wish it had been in Ada, a lot of issues in the
| system wouldn't have happened in Ada, but they did eventually
| get worked out anyways.)
| rch wrote:
| What's the state of Rust/C++ interop currently? Naively it
| seems like allowing Rust in C++ projects could be a reasonable
| way forward, but I haven't tried it personally yet.
| xscott wrote:
| I think you're implicitly saying that Rust solves the
| problems in the article, and so migrating C++ projects to use
| Rust is a path forward. However, the blog author's first
| example was implementing a "contiguous circular queue", so
| you should try to do that in Rust. Of course Rust already has
| VecDeque<T>, but then C++ already has std::deque<T> too
| (although not promised to be contiguous). So the exercise is
| to implement your own, from scratch, without just wrapping a
| type that does the dirty work for you.
|
| If you look closely at the implementation details for
| VecDeque<T>, you'll find a lot of complexity you might not
| expect. To do it efficiently and correctly, you _need_ to
| work with unitialized memory. So there will be unsafe blocks
| in there. A VecDeque <T> is built of a RawVec<T>, which uses
| a Unique<T>, which finally has a pointer, but also contains a
| magical PhantomData<T>. Look at the code [0] [1] [2] [3],
| look at the implementation details, read the comments, and
| assess for yourself if it's easier or harder than C++.
|
| Later in the article, he talks about exception safety. For
| something like a container, the most likely exceptions are
| that you've run out of memory, or you can't
| move/copy/construct an item in the container. I'm honestly
| not sure how Rust's builtin containers handle these problems.
| (Panic?) But if you're comparing the two languages, the
| apples and oranges matter.
|
| All of this to say that Rust is not simple either. You can
| program Rust by using its standard collections, and you can
| do the same with C++. If you try to implement those
| collections, say for learning/teaching data structures, both
| languages seem painfully complicated to me.
|
| [0] https://doc.rust-
| lang.org/src/alloc/collections/vec_deque/mo...
|
| [1] https://doc.rust-lang.org/src/alloc/raw_vec.rs.html#52
|
| [2] https://docs.rs/ptr/0.1.0/src/ptr/lib.rs.html#39-47
|
| [3] https://doc.rust-lang.org/src/core/marker.rs.html#679
| ansible wrote:
| > _Later in the article, he talks about exception safety.
| For something like a container, the most likely exceptions
| are that you 've run out of memory, or you can't
| move/copy/construct an item in the container. I'm honestly
| not sure how Rust's builtin containers handle these
| problems. (Panic?)_
|
| As I understand it yes, the containers will panic.
|
| Part of the discussion about using Rust in the Linux kernel
| is what to do about failed memory allocations. And this is
| a problem more generally for Rust usage in embedded
| systems.
|
| So there may need to be support for alternative allocators
| or something else.
| steveklabnik wrote:
| Part of the key is that they're not really "built in."
| They're in the standard library. When you're in those
| contexts, you just don't use the standard library.
| Problem solved.
|
| Now, the standard library is also getting support for
| these things not panicking on allocation failure, and
| when that's ready enough for those that want it (Rust for
| Linux has pulled the changes into their tree, in my
| understanding) then they could use them.
|
| (I work on embedded systems with no dynamic allocation,
| Rust is great.)
| lazypenguin wrote:
| It's fine, works well enough. Challenges are passing data
| across the boundaries as the data types need to conform to
| both sides. E.g. using a string in both languages without
| doing copies means using a C-style string and now you've lost
| some expressiveness in both languages.
|
| Second challenge is smoothness of integration in build
| systems. In C++ you might use CMake, in Rust you use Cargo,
| so now who is authoritative? Do you use Cargo to build the
| C++ code? Do you build your Rust projects in CMake?
| cogman10 wrote:
| C++ doesn't have a well defined ABI. So the interropt between
| rust and C++ is mostly over a C ABI bridge. For the most
| part, that's not too painful.
| pcwalton wrote:
| cxx, the high-level Rust/C++ interoperability layer, is
| currently used in production at large scale.
| flyingfences wrote:
| In this aerospace company, we use C, no C++ allowed, to avoid
| all the extra footguns that get in the way of safety and
| certification. Rust will probably be the next choice on the
| scene, but I expect it'll take as long to come as Ada took to
| go.
| dathinab wrote:
| > Rust will probably be the next choice on the scene
|
| I'm not sure about it.
|
| I think Rust might be hard to certify for aerospace usage.
|
| Not because it's bad, but because it's still complex. Just a
| lot of negative side effects of complexity are contained due
| to the compiler checks and design.
|
| But that makes it harder to certify as I can tell.
| potbelly83 wrote:
| Off topic, but curious why Ada went?
| jvanderbot wrote:
| In this aerospace company, we use C, and sometimes a subset
| of C++ that is basically C+epsilon. That's all new, however,
| so each new project that goes that direction is walking a
| dark forest.
|
| We've got some Rust on the ISS, but honestly, the interest
| from the serious FSW folks isn't there.
| ziggus wrote:
| User finds bag of M&Ms difficult to open:
|
| "IS CANDY DOOMED?"
| nine_k wrote:
| I'd compare C++ more to tobacco than to candy: it once looked
| like a good idea, but now its side effects are considered
| dangerous.
| Subsentient wrote:
| As someone who uses both Rust and C++ frequently with a good
| understanding of both languages, I can say that Rust is for the
| mostpart more pleasant to work in, but Rust's handicap is that it
| makes working in raw pointers when you really have to quite
| painful.
|
| If you need full raw pointers with aliasing mutable pointers, the
| fact that fn drop() takes a &mut T is a problem that can only
| currently be worked around with fugly hacks, like nesting your
| data type in another pointless structure, not to mention the
| giant pile of ambiguous UB that you risk whenever pointers and
| references interact that could so easily be avoided if Rust would
| just implement an -fno-strict-aliasing switch, but the devs
| refuse on what appears to be ideological grounds. The result is
| that for unsafe Rust, you get about the safety level of C much of
| the time, because RAII and many language facilities are not safe
| to use. Unsafe Rust is currently so dangerous that I feel much
| safer reaching for C++ for some of it. Mutable reference
| uniqueness, I think, should be enforced by the borrow checker and
| NOT by pain of inescapable possible undefined behavior at the
| assembler level.
|
| Now for C++.
|
| C++ is lacking a _LOT_ of useful safety features that Rust comes
| with baked in, such as Mutexes owning their data, the borrow
| checker, a good, fast atomic reference counting type, and things
| that translate to more safety indirectly, like algebraic enums,
| assignable and movable arrays, tuples built into the language,
| etc. Generics in Rust, however, are significantly more painful to
| write due to the lack of duck typing. Perhaps the Rust devs felt
| that duck typing was too "dangerous" for templates, but because
| they're statically evaluated, these issues almost never
| materialize, and so in practice duck typed templates are almost
| always superior to Rust's generics.
|
| C++ is definitely more mature and pragmatic as a language, but
| Rust is probably better for systems programming overall, at least
| if they fix their goddamn unsafe UB stuff.
|
| As for move semantics, that book isn't for "what you MUST do to
| keep things semi-safe!" though it contains some stuff to that
| effect. It's more about optimization and tricks from what I can
| see. Move semantics take a little while to wrap your head around,
| but once you know the basics of C++'s move semantics, you can
| pretty much figure out what's going to happen by looking at a
| class' definition briefly.
| PaulHoule wrote:
| I wish it was but I'm afraid it's not.
| extrememacaroni wrote:
| I dunno if it's doomed or not, but I picked up the language again
| recently for a project of mine and whenever I look at
| cppreference.com for any of the newer features I feel like an
| idiot. Feels like studying for college exams all over again.
| Smart pointers is as far as I'm gonna go with the post C++03
| features, miss me with all the template stuff and compiler
| tetrises.
| forrestthewoods wrote:
| > Is C++ Doomed
|
| I wish. Unfortunately I think it is we who are doomed and stuck
| with it forever.
| henning wrote:
| > Let's say I want to know if a constructor failed
|
| This seems like the beginning of a bad situation. I mean, this
| shows why constructors that do any complex work that can error
| out are a bad idea.
| AnimalMuppet wrote:
| No, they're fundamental to how C++ is idiomatically used.
| "Resource acquisition is initialization". This means that, if
| you have an object, and creating that object can fail, then it
| should fail in the constructor. So you either have a _valid_
| object, or none at all - you _never_ have an invalid object.
|
| Now, you could squint and say that an optional<T> returned from
| a factory is a valid object; that is, it's a valid optional<T>.
| I can sort of twist my mind that far, but it's not idiomatic.
| (It might become so, I suppose...)
| qayxc wrote:
| How is a factory function _not_ idiomatic? It 's not as if
| C++ is a single paradigm language that encourages a singular
| style or paradigm like OO.
|
| RAII is a mess in certain circumstances (looking at you,
| Vulkan API) and sometimes straight up just doesn't work
| anyway.
|
| If your initialisation is prone to failure, don't do it in a
| ctor.
| henning wrote:
| This just shows what a bad idea RAII is. You're destroying
| opportunities for managing resources in bulk and coupling
| that to just gathering a group of related data under a single
| identifier.
| pjmlp wrote:
| Until HPC, HFT, GPGPU, compiler development industries select
| another workhorse, nope.
| daenz wrote:
| Don't forget gaming!
| pjmlp wrote:
| That is part of GPGPU. :)
| mk89 wrote:
| yeah but it sucks to become the new "cobol" - I hope not, at
| least, as I am learning now C++ after so many years and I
| really like it. But hey, I don't make money with it, so I can't
| talk about how frustrating it must be to debug some weird
| stuff, etc.
| jvanderbot wrote:
| C++ is doomed, but it'll add so many features to mimic the cool
| new things that it'll cease to be C++.
|
| TFA: "That is, of course, until you realise the design philosophy
| that is being followed. They literally just implement whatever
| the current fad in programming is."
| posharma wrote:
| I hear you.
|
| * Move semantics itself requires a whole book
| https://www.cppmove.com/.
|
| * RVO has always confused me. It was much simpler in the C++03
| days. Copying large objects by value was inefficient. So you
| always used references. Period.
|
| * Don't fully agree with the commentary on exceptions; it's the
| same in most other languages if you want resources to be
| freed/given back.
| wiineeth wrote:
| I used to be a C++ Fan Boy. I just started learning rust last
| week and noticed how old c++ is. All the good practices are built
| into rust that you need to specifically write in C++.
| Jyaif wrote:
| > IS C++ DOOMED?
|
| Yes, as are all of today's imperative programming languages.
| leni536 wrote:
| > I have two options, one is to pass in an in-out parameter, the
| other is to the throw an exception.
|
| These are not the only options. You can also return `optional<T>`
| or an other wrapped value from a factory function.
| angelbar wrote:
| hermitdev wrote:
| "Is C++ Doomed?"
|
| No. The FILE example is either bad, tired, lazy or shows lack of
| knowledge of the standard library. A better way of
| opening/closing the FILE with RAII is right in the example [0] of
| unique_ptr at the wonderful cppreference.com:
| std::ofstream("demo.txt") << 'x'; // prepare the file to read
| { using unique_file_t = std::unique_ptr<std::FILE,
| decltype(&close_file)>; unique_file_t
| fp(std::fopen("demo.txt", "r"), &close_file); if (fp)
| std::cout << char(std::fgetc(fp.get())) << '\n'; } //
| `close_file()` called here (if `fp` is not null)
|
| I'm sorry, I'm not trying to be negative towards the author, but
| you need a better example than opening/closing FILE if you're
| going to make a compelling argument about the looming death of
| C++.
|
| [0] https://en.cppreference.com/w/cpp/memory/unique_ptr
| morelisp wrote:
| But this also shows a lot of warts that mean RAII isn't "really
| working", as in it isn't reducing the mental load of resource
| management as far as we'd like.
|
| - You've needed to manually specify the deleter. Why do I need
| to invent unique_file_t myself? The stdlib is failing to
| support idiomatic language use.
|
| - Probably most critically, fp can be null! If A _is_ I, then I
| _is_ A, yet here we are with a resource in-scope but not
| allocated!
|
| - You've had to manually add a scope to handle it, and (unlike
| e.g. Python with or Java try) there's nothing per se to
| indicate that's what the scope is for. SBRM is supposed to be
| interesting because of how often our scopes align with
| lifetimes naturally; manually adding a scope isn't too far from
| manually calling a destructor.
|
| Please don't respond with the reasons "why" it is this way; we
| all know. That also doesn't change _that_ they are still major
| warts that undermine safe, clean C++.
| jcelerier wrote:
| > - You've needed to manually specify the deleter. Why do I
| need to invent unique_file_t myself? The stdlib is failing to
| support idiomatic language use.
|
| But you're not supposed to use fopen / fclose at all, in
| unique_ptr or not, so the language shouldn't encourage you to
| use those.
|
| They are just often shown in unique_ptr, because when making
| examples to teach the language, it's a quick and easy way to
| showcase how to wrap any kind of pre-existing handles, not
| only memory, through the unique ownership semantics of
| unique_ptr; any other example would be platform specific
| (say, HWND on Win32, GL contexts or who knows what). Everyone
| knows fopen/fclose.
|
| Basically, that unique_ptr thing is just here to show that
| it's possible to quickly ease porting 45 million lines of C
| to C++ without having to change every fopen into an
| ifstream/ofstream.
| nyanpasu64 wrote:
| Using a function pointer as a unique_ptr's deleter type, and
| passing it in at runtime, makes the unique_ptr fat (two
| pointers large). To avoid this overhead, I prefer passing in a
| default-constructible type with an operator() deleter function,
| and not passing in a value into the unique_ptr constructor (htt
| ps://github.com/nyanpasu64/qvgmsplit/blob/edcce6df391c15b...).
| A neat party trick is to use the decltype of a lambda (https://
| old.reddit.com/r/cpp/comments/rlvsq0/does_anybody_re...).
| CyberRabbi wrote:
| His point is that C code often calls into C++ code. This is
| always incorrect unless the C++ entry point is guaranteed not
| to throw an exception. This is especially bad since this is not
| automatically checked by default.
| bullen wrote:
| I think the first step is to explain to people that C++ is just C
| with more stuff, the original is way simpler and the ONLY real
| reason to use a C++ compiler is that most dependencies that you
| need (OpenGL, OpenAL etc.) uses C++ by default.
|
| That took me 3 years to figure out on my own, such a waste of
| time.
|
| In C++ defense namespaces, strings and streams do have some
| usefulness to them, but if I could use a C compiler to build a
| modern 3D MMO I would remove those in a heartbeat!
|
| Edit: I know there are construed ways to call the OpenGL API
| directly without wrapper, but GLEW (damn Windows) is too
| comfortable for me to switch yet... Eventually I might switch to
| TinyC and write my own OpenGL/AL... That or implement my own
| J2ME!
| Jtsummers wrote:
| OpenGL, at least, is a C API, not C++. If you're using it
| directly, there is no need to use a C++ compiler. EDIT: And I
| just double checked, OpenAL is also a C API.
| tubs wrote:
| OpenGL is a pure C api, absolutely not c++.
| isaiahg wrote:
| All those things are C APIs not C++
| zwieback wrote:
| It's true that implementing a standard library in C++ seems
| daunting but
|
| a) most C++ users don't implement stuff that goes into the
| standard
|
| b) you can tailor your data structures to the problem at hand
| instead of making it usable with every type and use case
|
| c) quoting Stroustrup: "There are only two kinds of languages:
| the ones people complain about and the ones nobody uses".
| sharikous wrote:
| b is a limitation. C++ makes writing good general code a
| nightmare so you reduce to taylor your data structures to your
| problem
|
| c is just folk wisdom. That quote is just untrue
|
| a is true but it has gotten to the point that implementing C++
| std libraries and compilers is so daunting a task that even the
| big vendors do not rush into it anymore. There is this growing
| fatigue that left only MSVC, gcc and llvm in the game, and even
| them are always late to the standard
| rowls66 wrote:
| Kranar wrote:
| >c is just folk wisdom. That quote is just untrue
|
| Nothing folk wisdom about it. It's not 100% true but the
| quote is meant to highlight that most of the languages that
| get highly praised tend to the ones where the only people
| using it are those who really love it. People don't use C++
| or JavaScript or other high demand languages because they're
| such wonderfully designed languages that they love, they use
| it whether they want to or not because it's the industry
| standard. Compare that to people who write Haskell, or D or
| other niche languages where the only people using those
| languages are the ones who really like it. If people had to
| use Haskell or other smaller community languages because they
| somehow became industry standards, I assure you'd be hearing
| tons and tons of complaining about performance issues, how
| buggy they are, how annoying it is to do X, Y, Z...
|
| As is you don't hear people complaining about Haskell or D
| because the people who find those languages to be annoying
| simply stop using them and there's little reason to write a
| blog post about it. The predominant reason to write a blog
| post about a niche language is to basically give it some
| degree of praise.
|
| >There is this growing fatigue that left only MSVC, gcc and
| llvm in the game,
|
| What do you mean by this? I'm not aware of any other language
| that has three independent implementations that are all kept
| up to date within a year or so, the only language that comes
| close would be Java but even that is mostly dominated by
| Oracle's implementation.
| zwieback wrote:
| okay, good observation on a
|
| apparently the quote is confirmed by the man himself, though:
| https://www.stroustrup.com/quotes.html
| ModernMech wrote:
| I think what the OP meant was that the sentiment behind the
| quote doesn't reflect reality, not that Stroustrup didn't
| say it.
| AnimalMuppet wrote:
| No, just as Betteridge's Law would lead you to suppose.
|
| This really boils down to "creating your own custom data object
| that plays nice in all scenarios is too hard". (Or, more
| cynically, too hard for the author - some of us don't find it
| particularly hard.) Does that mean C++ is doomed? No, it just
| means that not all C++ developers are going to create objects
| with full RAII initialization semantics and full rule-of-five
| acts-like-an-elementary-data-type behavior.
|
| And that's fine! Not every object needs that. And if your object
| doesn't need that, you don't have to put it in.
|
| The problem, of course, comes when you have an object that
| _needs_ all that, and you don 't know how to do it. But that's
| kind of true for _every_ feature you don 't know how to use, in
| _every_ language. So are all languages doomed? No.
|
| Now, you could make a case that creating objects that have all
| those behaviors is the ideal in C++, and it's too hard to achieve
| that ideal. But hyperventilating about how that makes C++ doomed
| doesn't do anything to actually make that case.
| gigel82 wrote:
| No
| isaiahg wrote:
| I believe it's inevitable. But I would have given a very
| different answer even a few years ago. What strikes me the most
| though is that C++ is that it's not being succeeded by a new
| better C++ but instead it's being slowly replaced by multiple
| languages at once. C#, Java, Go, Rust, Swift, and JavaScript,
| (and maybe even python) are all replacing C++.
|
| In 22 if someone wants to make a general desktop app they'll
| probably use web technologies, or python, or java. If you do
| server development it's Python, C#, Java, Ruby, or Go. Embedded
| development is predominantly pure C. C++ is used but less often.
| Gaming is still a singular C++ affair but I'm starting to see C#
| make headway and now Rust is gaining traction. I've seen more
| game developers excited about Rust than I have about anything in
| the last 5 years.
|
| You can see it happening slowly, as more and more technologies
| are coming out and chipping away at it's use cases. Unless you're
| targeting game consoles, or a very specific rare domain, using
| C++ might be more of a handicap with so much better options out
| there.
| jcelerier wrote:
| > In 22 if someone wants to make a general desktop app they'll
| probably use web technologies, or python, or java.
|
| I wonder what apps you are talking about. The immense majority
| of apps I use are Qt. Just last week I was trying some newish
| Intel GPU profiler GUI:
| https://www.intel.com/content/www/us/en/developer/tools/grap...
| ; a Qt app. Last month I produced a video, with Da Vinci
| Resolve, also a Qt-based software.
|
| On the other hand, on my whole desktop I literally don't have
| any Java apps installed at the moment, like, not even a JDK.
| There may be a 2012-era copy of Minecraft in some backup
| folder. Likewise for Python, at most some apps will embark
| Python scripting around a C++ core like Blender but I don't
| think I use any app where the main() is written in Python.
| cenny wrote:
| I'm no C++ expert. But Wouldn't a private constructor and a
| static method that returns nil if input is bad fix this problem?
|
| Joshua Kerievsky talks a bout this problem in his book
| [Refactoring to
| patterns](https://www.programmingbooks.dev/#refactoring-to-
| patterns), specifically the refactoring "Replace Constructors
| with Creation Methods"
|
| That is maybe beside the point of the articles argument he is
| making about C++ being bloated and hard to use and read
| -\\_(tsu)_/-
| brigade wrote:
| Then you can't allocate the object in-place (e.g. on the
| stack), and also you have to ban copy assignment/construction.
| Probably a more common solution is like what STL's own fstream
| does - don't throw an exception on construction, and have a
| "not valid" state.
| titzer wrote:
| Something that I have learned is that we build abstractions to
| hide complexity. In so far as abstractions are not leaky and are
| fast, all is good. The moment you have a bug, or the moment you
| want to analyze performance, abstractions absolutely puke their
| f'ing guts at you and all that smart stuff someone thought no one
| would ever see spills all over the floor and it's a flat-out
| mess. C++'s pervasive unsafety and pervasive obsession with
| performance are the worst kind of witch's brew for plunging you
| straight into a hell of unimaginable depths at the slightest
| mistake.
|
| In particular, undefined behavior is so pernicious that it
| exposes literally everything about the machine and the entire
| program at once--its memory layout, compiler optimizations, and
| whatever else you weren't supposed to see. You are staring at a
| whole pile of hot garbage. There are literally no constraints on
| what the entire system is required to provide to you as aid to
| debug the problem. Undefined behavior literally gives _no
| meaning_ to the entire execution of the program since its start,
| no matter where or when the undefined behavior occurred. There is
| no "before" UB and "after" UB--the entire execution is invalid.
| You are in a parallel universe that is not required to make
| sense.
| coliveira wrote:
| C++ has its problems, but it has two advantages to other
| languages: (1) it is evolving and extensible at a deep level: one
| can identify strategies and create solutions based on the
| template system, because that's how the standard library itself
| is created. (2) it is backward-compatible, which means that we
| can continue to support the incredible amount of software already
| available for C and C++.
| johannes1234321 wrote:
| While that is true, this in turn means that the result is
| complex. You have old and new ways to do things, and new things
| are complex with all the integrations into the different parts.
|
| Unified initialization sounds great, always use brace
| initialisation and be done, you think ... but no it's complex.
|
| Or move semantics and universal(?) references are introduced to
| solve efficiency problems and make things nicer. For simple
| stuff it works, but oh the mess becomes wild.
|
| How well modules will work out is still to be seen. Suddenly
| you need the build system to be a compiler and can't parallel
| build everything anymore, but have to create a dependency graph
| to build in order, while the standard treats that as a
| implementation detail.
|
| C++ has some great features, however it would need a big
| cleanup, a cleanup which would break all code, worse than
| Python 2 vs. Python 3, in a world where other environments and
| languages are viable alternatives. Thus C++ can't afford the
| big jump.
| dathinab wrote:
| Both C++ and Rust are rather complex.
|
| But in Rust the negative effects of the complexity are mostly
| contained by the compiler (i.e. if you get it wrong you get a
| compiler error).
|
| In C++ it on the other side can easily lead to UB and in turn to
| potential security vulnerabilities.
|
| So IMHO today there are _very_ few cases where C++ is still the
| appropriate choice.
|
| Just to be clear I'm not saying rust did replace C++, but there
| is a wide variety of languages which are a better choice for many
| use-cases which once where filled by C++. Rust just further
| reduces use-cases where I would say C++ is appropriate.
|
| But it's anyway not Doomed as there are just way to many existing
| projects. Not just legacy projects, but existing still maintained
| for many years projects.
| lazypenguin wrote:
| On a long enough timeline I do believe C++ is doomed or will
| otherwise become something completely unrecognizable from its
| current form. Similar to how C++98 code is wildly different than
| modern C++ code. People often ask, "is C++ a good language?" or
| "is XXX better than C++?". Doesn't really matter, what matters is
| whether people are willing to pay the "tax" of
| learning/using/maintaining C++. C++ is a generalist language and
| new languages are competing with it by specializing in different
| areas. If I am going to use something as unergonomic and perilous
| as C++ it needs to be for some advantage but looking into the
| future and comparing to newer language, that advantage is
| becoming less clear.
| soup10 wrote:
| C++ is not a generalist language, its strengths are high
| performance, direct memory access and native compilation. If an
| application doesn't need any of that, a higher level language
| is usually better.
| tehjoker wrote:
| The commentary is good, but the conclusion is unsupported. These
| are annoying issues, but doom they do not make.
| quantified wrote:
| Define "doom" or "death". Cobol still runs, FORTRAN still runs,
| not the coolest in the TIOBE index but in the tail. A mammalian
| body doesn't die all at once, either, but past some threshold
| of vitality does definitively die and remains dead. Really
| unclear whether these languages will be dead until the last
| compiler stops compiling. So the discussion needs a definition
| for the threshold the language may reach.
| tehjoker wrote:
| I think "dead" is pretty clear. The work is not used anymore
| at all, or only for hobbyist projects.
|
| I think "dethroned" or "unpopular" would be more correct
| presuming that C++ would lose popularity over time due to the
| issues raised.
| nyanpasu64 wrote:
| In my C++ code, I've copied Rust and expressed fallible
| initialization by returning a std::optional<T> from a static
| method (which the article failed to mention). The problem is
| that (like Rust) you lose placement initialization, and (unlike
| C++ _or_ Rust) you can 't initialize private fields using
| aggregate initialization or initializer lists, and must write a
| passthrough constructor (which can't even be private because it
| breaks make_unique).
|
| https://github.com/hsutter/708 is a C++ proposal which unifies
| placement constructors and writable out-parameters ("definite
| first use"). I don't _think_ it makes placement initialization
| fallible, but I 'm not sure.
| mark-r wrote:
| Have you heard the phrase "death by 1000 cuts"? That's what
| programming in modern C++ is like. I agree they don't quite
| make the case for "doom" but it's still not a pretty picture.
| qayxc wrote:
| This image is mostly painted by people who forgot to stop.
|
| By that I mean stop using every feature and detail because
| it's there.
|
| C++ can be used like a much better C (smart pointers,
| references, const-correctness, generics via templates) and
| most of the complications can be ignored that way.
|
| Premature optimisation is another big factor. If there's no
| measurable performance bottleneck, things like move-semantics
| don't even matter in practise for example.
| zibzab wrote:
| Having followed SerenityOS for some weeks, I now believe C++ is
| doing fine.
|
| Also, that cookie pop-up on WordPress gave me cancer.
| Const-me wrote:
| > I have two options, one is to pass in an in-out parameter, the
| other is to the throw an exception.
|
| Another one would be not using constructors at all, instead
| implement initialize() or create() method in the class.
|
| > Now you have the issue that every single operation can throw an
| exception
|
| Depends on the code. I tend to avoid exceptions when I can.
|
| > you need to write code that wraps every possible resource in
| RAII logic.
|
| That's generally a good idea regardless of whether exceptions are
| used or not. Without RAII for file handles, next day/month/year
| another programmer will write `if(condition) return;` and leak
| the handle.
|
| > And when it comes to implementing move and copy constructors
| you are left feeling like you've definitely done something wrong
|
| For many classes in my code, I disable both with =delete; copy
| constructor. This makes a class which can be neither moved nor
| copied, which simplifies things substantially. For instance, if
| you need a RAII equivalent of CAtlFile [0] over FILE* from
| stdio.h, immovable file handles might be a good idea. Another
| thing, if the class owns many gigabytes of data in some
| collections, you wouldn't want to copy anyway, too expensive. In
| some cases I want to move such objects, for that I define a
| swap() method. An explicit swap() method is IMO more readable
| than std::move.
|
| For many other classes in my code, I declare no constructors at
| all because the default compiler-generated ones already doing
| what I want.
|
| [0] https://docs.microsoft.com/en-
| us/cpp/atl/reference/catlfile-...
| Bigpet wrote:
| To the point of complexity, the allocation makes the container
| unfit for over-aligned types. But honestly I don't even know if
| STL containers are over-alignment aware.
|
| So instead of (T*)malloc(sizeof(T) * capacity_);
|
| it should be something like this if you want to deal with over-
| aligned types. (T*) operator
| new(sizeof(T)*capacity_, std::align_val_t{alignof(T)});
|
| Also, in the reallocation function the elements are move-assigned
| to uninitialized memory. They should be move-constructed with a
| placement-new. And of course calling the destructors of the
| moved-from objects is still required.
|
| It's of course a little telling that the compiler will not
| complain about these errors and you have to use and know to use
| tools like address sanitizer, memory sanatizer and valgrind to
| let a machine help you find them.
| Ekaros wrote:
| Define doomed? Not being around in century? It certainly have
| enough inertia to carry on for long time, probably decades.
| qayxc wrote:
| I am very confused.
|
| Some of these ramblings are just plain wrong, like
|
| > But exceptions come at a performance cost.
|
| No they don't. An exception that isn't thrown costs nothing.
|
| Also
|
| > Let's say I want to know if a constructor failed. I have two
| options, one is to pass in an in-out parameter, the other is to
| the throw an exception.
|
| Using a factory function instead is a third option that avoids
| this. Besides, this is a false dichotomy to begin with - it's
| perfectly acceptable to simply record the validity as part of the
| object's state and provide access to that (especially if the
| state established in the ctor isn't an invariant). So there's
| more than just two options.
|
| Then there's more confusing arguments like the custom FILE
| wrapper. So instead of just using std::unique_ptr<std::FILE,
| decltype(&close_file)>, i.e. the idiomatic solution for the past
| decade that the author claims to be after, they blame their
| C++03-style on the language? I don't understand.
|
| Fortunately they don't provide any concrete examples of "features
| bleeding into each other", since I suspect that would only
| further illustrate their lack of understanding and knowledge
| gaps...
|
| They end with
|
| > I'm not in the business of writing the perfect program that
| satisfies an arbitrary standard. I'm in the business of making
| stuff with tools. [...]
|
| Well great, then don't use C++ if you're not productive with it.
| Yes C++ is a complex language. A big portion of its complexity
| can be avoided by simply not using features one doesn't need (or
| understand) or by limiting oneself to a reasonable subset of said
| features.
|
| The author seems to look for something like Go instead. C++
| wasn't exactly "designed" and is the result of a steady evolution
| that kept some legacy baggage and bad earlier decisions around.
| ghosty141 wrote:
| To add to "Let's say I want to know if a constructor failed."
|
| Constructors are simply not meant to fail. You should never do
| something like io in a constructor. For everything more
| complicated than setting some fields a factory is better
| suited.
| nmilo wrote:
| Why? Isn't one of the ideas of C++ that every constructed
| object is in a valid state? Even the STL has constructors
| that can fail, and it does it through member variables/flags,
| which doesn't seem like a very good solution:
| std::fstream s(filename); if (!s.is_open()) {
| std::cout << "failed to open " << filename << '\n'; }
|
| No one should ever have to use factories, they really suck.
| Too much boilerplate, too much code duplication.
| jcelerier wrote:
| how do you do non-trivial dynamically-loaded plug-ins
| without factories ?
| dataflow wrote:
| > Constructors are simply not meant to fail. You should never
| do something like io in a constructor. For everything more
| complicated than setting some fields a factory is better
| suited.
|
| How would you reconcile that with the fact that
| std::vector<T>::vector() is explicitly permitted to fail? [1]
| Do you construct your std::vector<T> objects with factories
| too?
|
| > Exceptions: _Calls to Allocator::allocate may throw._
|
| [1] https://en.cppreference.com/w/cpp/container/vector/vector
| knorker wrote:
| > The author seems to look for something like Go instead
|
| But in Go you also have to write exception safe code, something
| the author complained about.
|
| Before someone says "but you're not supposed to throw or catch
| in Go": the std http server swallows exceptions thrown in
| handlers, and fmt.Print does for String callbacks too.
|
| And before someone says Go doesn't have exceptions: It does,
| 100%, in all but name.
|
| I actually find C++ to be way more designed, though through,
| and consistent than Go.
|
| That's not to say that Go isn't a vastly simpler language. It
| is. And C++ has way more "just because you can doesn't mean you
| should".
| nmilo wrote:
| > And before someone says Go doesn't have exceptions: It
| does, 100%, in all but name.
|
| But here the name actually matters. They're called "panic"s,
| not exceptions. When you panic, you signal a fatal error that
| should ideally terminate the program. You shouldn't care
| about closing files or freeing memory, because well-written
| Go does not panic, and if it does, execution will end very
| soon. A panic in a String() method is just bad code, don't do
| it. Meanwhile in C++ you can reasonable expect an exception
| anywhere. Bad arguments, invalid states, IO failures, and
| other errors that should be expected in the execution of a
| program all throw exceptions in idiomatic C++.
| Rusky wrote:
| > An exception that isn't thrown costs nothing.
|
| It costs binary size for unwinding tables, and in some cases it
| is an optimization barrier.
| jcelerier wrote:
| and yet when benchmarking, exceptions are often faster.
| cross-posting a comment I just made on r/programming: https:/
| /www.reddit.com/r/programming/comments/sz86fl/comment...
| UncleMeat wrote:
| C++ exceptions aren't totally zero overhead. For an obvious
| example, you pay in code size.
|
| I do agree with the overall analysis, though.
| CyberRabbi wrote:
| > That means you need to write code that wraps every possible
| resource in RAII logic.
|
| This is a great point. It means that you cannot mix C and C++
| code without care, which seems like a huge design smell to me
| especially since this is not something that can easily be
| verified statically.
|
| Using C++ with exceptions disabled is the only foolproof way to
| run it in a large heterogenous application.
| AlexandrB wrote:
| > Using C++ with exceptions disabled is the only foolproof way
| to run it in a large heterogenous application.
|
| Is there a way for a ctor to report failure in newer C++
| standards or are exceptions still the only way[1]?
|
| [1] https://www.yosefk.com/c++fqa/exceptions.html#fqa-17.2
| frozenport wrote:
| Practical implementations of data structures require
| consideration of memory layouts and pools which have real
| equivalent in other languages.
|
| For example, imagine building a graph structure to hold a uint8
| field but calling malloc for every node. You get that and worse
| in Python.
|
| Simply put you cannot write performant, non-toy data structures
| outside of low level languages like C++.
|
| I think OP is simply not aware of the kind of structures that are
| used in algorithm critical applications, like CAD, compilers,
| etc.
| nine_k wrote:
| C++ is as doomed as Fortran.
|
| Even if you look at it as at a strictly legacy technology, this
| legacy will be around for a _looong_ time. A lot of important
| code is written in C++, without any viable replacement.
|
| Also, like Fortran, it will keep improving even while relegated
| to its niche, and will become much nicer (yes, even nicer than
| the latest standard).
| ridiculous_fish wrote:
| OP is trying to create a circular queue by using malloc(). This
| may be a good learning exercise, but the code could be made much
| simpler by just wrapping `std::vector`. Working with raw
| uninitialized memory is difficult in any language.
|
| Most of the article was about C++ exceptions specifically, and I
| agree C++ exceptions are probably "doomed." They are already
| disfavored by major codebases (Google, LLVM, others). It is
| indeed unreasonably hard to make exception-safe C++ containers.
|
| One possible future path here is Herb Sutter's Zero Overhead
| Deterministic Exceptions [1] which "throws" values through the
| ordinary return path, as is done in Swift/Rust/Go etc.
|
| I don't know how `bad_alloc` will get handled, but I'm not sure
| how useful that even is any more, given Linux memory overcommit.
| Curious to know if anyone takes bad_alloc seriously?
|
| > Let's say I want to know if a constructor failed
|
| Usual advice here is "don't write fallible constructors." Use a
| factory function instead.
|
| I can't agree with some of the other points. I don't see how
| "copies that result from calling a function have a different
| meaning to regular copies." RVO became guaranteed in C++11, and
| you don't need to worry about value categories when implementing
| move or copy constructors. C++ is not an easy language but it
| does have rules. And most of the time this stuff can be ignored:
| if I copy a string an extra time it usually doesn't matter.
|
| 1: http://www.open-
| std.org/jtc1/sc22/wg21/docs/papers/2018/p070...
| reincarnate0x14 wrote:
| I hope so, because I irrationally decided I didn't like it versus
| Objective-C a long time ago and I can't wait to be vindicated :)
|
| But even if it is doomed, given inertia it will still be a
| mainstay of HPC and such long after Rust or whatever takes the
| crown as the language people take the time to complain about, to
| paraphrase Stroustrup.
| iamflimflam1 wrote:
| You'll have to prize C and C++ from the cold dead hands of the
| embedded programmers. Most of them are still doing OO using plain
| C.
| nine_k wrote:
| C, yes! C++?
|
| I also think that Zig has a good chance to become popular in
| the MCU space.
| colejohnson66 wrote:
| The Arduino and friends can run on C++. The Arduino "IDE"
| uses "C _almost_ ++", but C++ compilers for AVR and ARM
| boards do exist.
| bodhiandphysics wrote:
| I should note that writing data structures in rust is also a
| fraught business! In practice, that's just a property of
| languages that don't have garbage collection.
|
| Exception safety is a serious complication in c++! Exceptions
| were a mistake in the language, particularly given the existence
| now of std::optional.
| bodhiandphysics wrote:
| Finanally, c++ is the only language I would ever want to do
| linear algebra in, or for that matter, many graph algorithms.
| Rust is adding features to make it useful for linalg, but it's
| not their yet. Linear algebra is important!
| chrisseaton wrote:
| > Finanally, c++ is the only language I would ever want to do
| linear algebra in
|
| Isn't something like MATLAB better for linear algebra than
| C++?
| Jtsummers wrote:
| Or Fortran or Julia.
| bodhiandphysics wrote:
| I don't want to use Fortran! Julia is lovely
| bodhiandphysics wrote:
| Low level linalg... implementing solvers using openmp and
| CUDA
| [deleted]
| beached_whale wrote:
| that's just false. Exceptions were not a mistake and
| std::optional isn't even a good type to replace more than
| communicating that there is no value. It's as good as returning
| nullptr, in this case. And what strength of an exception
| guarantee does one really need? A basic guarantee is not that
| bad to give and most things do not need to worry if they don't
| use unmanaged resources without RAII types(e.g pointers).
|
| But the big thing is, a large amount of code does not have
| error states at all, they always work. Another chunk have
| preconditions, that are generally developer errors, that
| shouldn't be throwing either. That leaves the parts that
| interact externally. And here is where one learns, it's not a
| dichotomy of exceptions or error flags/results... it can be
| both. It's more to do with the shape of the code.
|
| One example is, one is passing a string buffer to a parser, an
| exception is probably fine as the error cannot be dealt with
| within the parser. It didn't create the malformed document and
| cannot fix it. To use error types will greatly complicate the
| inner workings of the library as it has to deal with errors at
| a majority of calls internally. This leads to more
| bugs(assuming more code == more bugs) and complexity/branches.
| One already paid for the branch to check for the error in the
| document too, now they have to pay for branching up the stack.
| Exceptions strong part is that they cannot be implicitly
| ignored. One can do that with error types that force a read
| prior to destruction too though.
|
| For short distance things, where one is handling the error
| state after the call, not throwing is fine.
| edflsafoiewq wrote:
| Rust has the same exception safety issues. Eg Vec::retain_mut:
| https://github.com/rust-lang/rust/blob/532d3cda90b8a729cd982...
___________________________________________________________________
(page generated 2022-02-23 23:01 UTC)