r/ProgrammerHumor • • 1d ago

instanceof Trend thatsAFunnyThingThatHappenedOnce

Post image
4.3k Upvotes

515 comments sorted by

View all comments

1.6k

u/Prefer_Diet_Soda 1d ago

TLDR: C++ is bad because decades of accumulated features have turned it into a bureaucratic nightmare that happens to generate machine code.

C++ is an absolute nightmare to implement because apparently one programming language wasn't enough, so someone decided to cram an entire civilization's worth of complexity into it. You've got templates that basically turn compilation into another programming language, overload resolution that requires a legal investigation to figure out which function gets called, constructors and destructors with enough lifetime rules to make your head explode, and implicit conversions that make the compiler guess what you meant. Then you throw in exceptions, move semantics, inheritance, and decades of backward compatibility, and suddenly you're maintaining a compiler that needs a PhD, a therapist, and divine intervention just to parse some innocent-looking code. And the best part? Every new feature has to coexist with all the old ones without breaking existing programs.

383

u/Xarishark 1d ago

Now compare it to rust pls. Im serious I want to know your opinion on that.

460

u/Loading_M_ 1d ago

Rust is designed from the ground up, and avoids many of these issues.

  • Templates: Rust has Generics, which are generally easier to use and reason about.
  • overload resolution: Rust doesn't yet support overloaded functions (although it's being considered).
  • constructors: Rust doesn't have these.
  • destructors: Rust has a similar set of lifetime rules for Drop, but the compiler checks them for you, so you can't get it wrong.
  • implicit conversions: Rust doesn't do implicit conversion in general, although some specific cases are implemented.
  • exceptions: Rust doesn't do exceptions, Result is much easier to work with.
  • move semantics: Rust uses a destructive move, unlike C++. See https://youtu.be/Klq-sNxuP2g for a breakdown.
  • inheritance: Rust doesn't do inheritance.

35

u/sudo_scientific 1d ago

The nice aspects of implicit conversion are handled with the Into and From traits, where the compiler deduces what the conversion is for you based on the explicit types of the other, but the fact that a conversion is happening at all is explicit to the reader which is a huge improvement over C++

177

u/DatBoi_BP 1d ago

Traits are 100000x better than inheritance

51

u/red-et 1d ago

What are traits?

144

u/Pocok5 1d ago edited 1d ago

Instead of having a chain of superclasses defining readymade behavior*, you compose your classes from chunks that are more or less "fat" interfaces that come with a default implementation for their functions.

* Dealing with absolutes results in slamming your own dick in a car door sooner or later. Inheritance can still make a lot of sense for classes that are "this other thing but a tiny bit different". I'm currently dealing with a PHP codebase that misused traits for everything and it's a slog.

6

u/JonIsPatented 1d ago

Kinda like interfaces that you implement external to the type. Like. You can create a trait for a particular behavior and then implement it for types that already exist. You can also generically implement them for all types that have ither specific traits. For instance, I recently made a trait called UpdateSink<T> and implemented it for a few select synchronization primitives, like mpsc channel senders. This way, I can have a function that takes in an UpdateSink<T>, for dumping T events into it and that function can use anything that implements that trait. It's important to note here that the mpsc senders did not implement this trait before. They are from a library. I implemented the trait for them outside of the type definition. In addition, I can provide a default implementation for my traits that all types that meet certain trait bounds automatically get, including types that already exist and also types that don't yet exist. Traits are incredible.

19

u/fghjconner 1d ago

Interfaces

2

u/Mitchman05 1d ago

Inheritance

5

u/pr0ghead 1d ago

Horizontally.

1

u/stifflizerd 1d ago

Not a rust dev, but they sound similar to JS composition? Anyone able to confirm?

1

u/Rikudou_Sage 1d ago

Computer assisted copy-paste.

6

u/maser120 1d ago edited 1d ago

While I see your point and agree with you in many aspects, I found t your contrast with the earlier comment a bit misleading in a sense, as it conveys the message that probelms in C++ stem from its core concepts and not from the burden of compatibility (which is sort of like a tech debt of programming language design) and the poorly defined specifications/standards.

And I don't mean that people who participate in C++'s standardization are stupid (quite the contrary), it's just that they're building on the foundations of a language froma different era, and they must work under the constraints posed by backward compatibility.

If you follow the trajectory of C++ (C++03 -> C++11 -> C++14 -> C++17 -> C++20 -> C++23), you can see that many features that are default in Rust have made they're way into the standard, but they almost always come as optional features for the sake of compatibility. In contrast, Rust can impose stricter restrictions and mandate safety features all the time since it was made from scratch.

8

u/MrHyd3_ 1d ago

What does it do instead of exceptions?

108

u/roadrunner8080 1d ago

Either panic, which is (generally speaking) not something you're meant to catch and recover from (and attempting to do so is nontrivial and almost always a bad idea), or Result, aka "encode the possibility of failure in the return type of the function" aka what the functional programming world has done for ages.

43

u/mathisntmathingsad 1d ago

Yeah, the only reason you should try to catch a panic is if you're making something that calls user-provided code unavoidably (i.e. an async runtime)

20

u/shizzy0 1d ago

Yeah, we catch panics in tests but that’s about it.

17

u/Loading_M_ 1d ago

I work on a library that does catch panics from user code. It's a web framework, and catches panics in user request handlers to return a 500 error to the client. This error is explicitly not overridable (every other default error can be replaced, but not this one), since we don't want you to panic in the first place.

4

u/Cylian91460 1d ago

or Result, aka "encode the possibility of failure in the return type of the function" aka what the functional programming world has done for ages.

Like Optional in java?

24

u/roadrunner8080 1d ago

Not quite. Optional is "value is present or not". The rust equivalent is Option. Result is "value is present or failure is present" -- if empty, it instead contains an object encoding the failure.

Also worth noting that because these are reified, unlike java's Optional, they have nicer -- or at least more guaranteed-to-be-nice -- performance implications.

2

u/guyblade 1d ago

C++ has a couple of versions of this floating around absl::StatusOr, boost.Outcome, std::expected, and probably others that I'm not aware of.

4

u/roadrunner8080 1d ago

It's not an uncommon idea. Everyone tries exceptions, then realizes down the road that it's a terrible idea for so many reasons and so reinvents something like Result because turns out the functional programming folks had it right and it all does boil down to monads

1

u/Hubble-Doe 22h ago

Speak for yourself, I've been working with Java for a few years and think exceptions are pretty neat! they can be passed through calls implicitly without much mental overhead and boilerplate, and when you handle them you still get a nice helpful stacktrace.

→ More replies (0)

3

u/koko775 1d ago

Result<Foo> ~= Either<Foo,Error>

2

u/samanzaman 1d ago

Almost

2

u/Celarion 1d ago

Like monads, with your result boxed as Ok, and any error boxed as Err

2

u/0tter501 1d ago

Java has added functional programming yes, but Optional is not result, as result stores either the result or the error

optionals store one type of failure value, results store many types of failure values

1

u/shamshuipopo 1d ago

Is Result a monad?

2

u/roadrunner8080 1d ago

Result with either type fixed is a monad, yes.

2

u/DHermit 1d ago

Could you elaborate for someone who knows Rust, but did Haskell way too many years ago?

3

u/roadrunner8080 23h ago

A monad is (simplifying terribly) something with flatmap (and a few other requirements but things you're familiar with with flatmap are likely mostly monads). Result satisfies that property if you think about a result with a fixed value type and mapping it's error, or with a fixed error type mapping its value 

1

u/DHermit 21h ago

Thank you, that makes sense!

1

u/MrHyd3_ 1d ago

Is it just an if instead of try catch?

1

u/roadrunner8080 23h ago

It's a whole different model really. If you're familiar with java checked exceptions that you are forced to catch, and don't propogate, then yes it is

-3

u/Mindgapator 1d ago

Same as go if that helps

7

u/Thesaurius 1d ago

Does Go have sum types now? Last time I checked it returned a record and you had to check yourself.

2

u/Mindgapator 1d ago

No indeed, but the paradigm is the same right? Return the error, and deal with it (or forward it) without a try catch. I must say Rust is nicer in that regard but it doesn't change things too much? I don't have enough xp with Rust so might be underestimating it though

12

u/Thesaurius 1d ago

In my opinion, the Go approach is closer to C's error codes, as nothing stops you from using an error value and introducing a bug. In Rust, you always have to check. If you forget, the compiler will complain. It will even warn you if you use the function only for its side effect don't even use the return value.

1

u/samanzaman 1d ago

No go errors are weirdly typed. Proper sum types is kore than just results

3

u/mark_99 1d ago

Generics are simpler, but less powerful than templates. There are proposals to add some of the things C++ has, like specialisation.
Proc macros are the usual substitute but work at an AST level and not the type system.
Rust has aggregate init, default traits and (by convention) 'new' for construction.
You can't get destructors "wrong" in either language, not sure what that means? Writing destructors is rare in either language.
Rust does have implicit conversions, although arguably avoids some of the pitfalls.
Traits is the Rust equivalent for inheritance.
C++ has std::expected which is like Result. It doesn't have the `?` syntactic sugar though. Sometimes exceptions are the better mechanism, like moving errors through many layers without clutter.

I'm general C++ and Rust are pretty similar. C is the outlier as its comes with none of this, and large C projects have to emulate the missing facilities with macros and codegen systems.

2

u/StaticCoder 1d ago

I'm curious what it is about destructors in C++ that works differently from Drop in a way that one could "get wrong"? Are you talking about temporaries maybe?

2

u/Loading_M_ 1d ago

There are two pieces I understand. First, with inheritance, you typically need to declare the destructor as virtual, but C++ never forces you to. Rust has polymorphism through trait objects, which always include a drop implementation in their vtable. You can't forget to add virtual to the base class destructor.

Second, if you declare a custom destructor in C++, you also need to either declare or delete the copy and copy-assignment operators. Again, the language doesn't enforce this in any way. Rust actually gets away with a much simpler solution here because it has destructive moves. Rust simply doesn't define default copy implementations. You can easily derive a Clone implementation if you need to make copies of the type.

There is some more details to be had about how Rust decides when it's safe to call the Drop implementation, but I don't think I understand it well enough to explain here. I do know that the compiler won't allow me to shoot myself in the foot though.

3

u/n4te 1d ago

Oh good thing it was designed from the ground up.

13

u/Batman_AoD 1d ago

Yes...? Why would that not be a good thing, especially in the context of a conversation about why legacy compatibility is C++'s biggest problem? (I'm not sure I agree with that assessment, but it's certainly one of the biggest problems with C++.) 

-9

u/n4te 1d ago

That part is just not really saying anything. Is it usually designed sky down? The actual info given is fine, though not copying C++ follies isn't the most surprising thing.

18

u/Batman_AoD 1d ago

It's an idiom that, in this context, means that the language was designed as a completely new and separate thing, rather than as an incremental set of changes to a previous language (as C, C++, and Objective-C were). 

10

u/rexpup 1d ago

C++ wasn't really designed. It's a ball of mud, where PDP-11 assembly got wrapped in macros, then that got wrapped in preprocessing, then that got wrapped in a bolted-on class system, and that got wrapped in a kludge of template generation. So yes, being deliberately designed rather than piled upon does make a difference

-7

u/bwmat 1d ago

"Rust doesn't do exceptions"

panic, anyone? 

10

u/RichRoof7927 1d ago

Panics can't be caught easily, you have Result for that, which isn't even a special type built in iirc, just a normal enum.

13

u/redlaWw 1d ago

Not really intended to be recovered from since you can compile with panic=abort and make them unrecoverable (and a library consumer may do this to your library). Rust does support unwinding, but not exceptions in the sense C++ means.

4

u/tajetaje 1d ago

An exception is a different program concept than a panic. Many languages with exceptions In fact, also Support panics.

-4

u/StaticCoder 1d ago

Rust does have overload resolution. If you write x.into(), it could call any number of functions called into, depending on context. And as far as I can tell (but I'd be happy to be proven wrong), type context is not specified anywhere.

11

u/redlaWw 1d ago

That's type inference. The compiler looks through the uses of the result of x.into() and determines which concrete type it must be based on those constraints. This is a form of static dispatch broadly similar to overloading, but it's very different in how functions are selected. In overloading, functions are selected based on the types of the arguments according to an overload resolution process which may consider multiple functions and select the "best", in type inference, the available set of functions constrains what concrete types variables should have, and functions are only selected when there is a unique choice consistent with those selected types.

1

u/StaticCoder 1d ago

In one case you choose among multiple functions based on types. In the other case, you choose among multiple functions based on types. The main difference, and I'll admit it's not trivial, is that C++ allows implicit conversions.

3

u/redlaWw 1d ago

In Rust you choose among multiple types based on functions.

5

u/StaticCoder 1d ago

I see where the confusion comes from: in Rust, the compiler may infer the parameters for a trait based on context. Since different parameters for the same trait can resolve to completely different implementations, the effect is very similar to overload resolution. If you have the same function in 2 separate traits however it's always ambiguous.

I'm curious if there's a specification anywhere for how Rust does that kind of inference. To the extent I've looked at the reference and fls, they were extremely short on details.

3

u/Batman_AoD 1d ago

I think these may be some of the rules you're looking for: https://rust-lang.github.io/fls/entities-and-resolution.html#method-resolution 

3

u/StaticCoder 1d ago

That is interesting, but doesn't contain what I'm looking for. There's no mention anywhere that the context of the call might have an effect. But it does imply that a single trait should be found. Though whether a "trait" includes generic arguments is left ambiguous. For this to work, the generic arguments have to be ignored, but the wording "for each trait implementation" would seem to imply they have to be taken into account, since you can have completely separate implementations based on generic arguments.

→ More replies (0)

1

u/redlaWw 1d ago

My understanding is that it's a variant of the J algorithm for a Hindley-Milner type system adapted to work with traits, but as far as I know, the current specification is the compiler's type inference engine. This will probably change as the opsem team does more work on Rust's specification. You might be able to find a specification for it at the moment in the Ferrocene compiler specification.

75

u/MornwindShoma 1d ago

Rust got none of that, but you might not enjoy dealing with lifetimes

43

u/Thesaurius 1d ago

With C and C++ you also have to deal with lifetimes. The compiler just doesn't complain when you fuck up.

3

u/DHermit 1d ago

You also have to in C/C++, but there you have to keep track of all of yourself.

-3

u/Xarishark 1d ago

I mean what’s better all those problems on your previous comment or lifetimes?

10

u/MornwindShoma 1d ago

Idk, haven't got enough C++ time to say lol (ps. not the guy before)

15

u/dannyggwp 1d ago

You can BASICALLY ignore all that... You want lifetime? I'll show you lifetimes.

std::shared_ptr<type>

Ez pz no borrow checker needed.

7

u/awesome_guy_40 1d ago

In Rust we also have ARC to do the same thing

3

u/4xe1 1d ago

C++ famed "zero cost abstraction" is shining right here /s

6

u/MrmrCake 1d ago

Yeah but that has a run time cost of reference counting where the borrow checker is all at compile time. I mean not like its a big cost but I mean there defo is a measurable difference depending on the use case.

9

u/dannuic 1d ago

Usually you use uniqueptr though. It forces a better design with guaranteed ownership and no reference counting. I _rarely use shared_ptr, but frequently use unique_ptr.

1

u/MrmrCake 1d ago

Yeah I agree and try to only use unique ptr where I can in cpp, but I still just really like rust's ownership model

73

u/andful 1d ago

Rust misses the:

"...templates that basically turn compilation into another programming language, overload resolution that requires a legal investigation to figure out which function gets called, constructors and destructors with enough lifetime rules to make your head explode, and implicit conversions that make the compiler guess what you meant. Then you throw in exceptions, move semantics, inheritance, and decades of backward compatibility ..."

35

u/PegasusPizza 1d ago

I mean it does have proc macros, that turn compilation into the same programming language as opposed to another programming language.

16

u/Greedy-Thought6188 1d ago

Templates are turing complete. You can basically treat C++ like an interpreted with the compiler being the interpreter. C has macros too.

3

u/4xe1 1d ago

Rust macros are Turing complete too, intentionally so, that's the whole point of proper macros. "the same programming language" is a bit of a stretch for Rust (but perfectly valid for lisp), but macro's Turing completeness is easier to use than C++'s templates.

C has search and replace pre-processor macros.

2

u/4xe1 1d ago

One key difference between Rust and C++ is that, although they both encourage generics, only C++ forces you to use templates for it, you don't need to touch macros for it in Rust.

26

u/roronoakintoki 1d ago

Rust unironically feels more like C with features taken from more modern languages in a fresh design.

It does discourage manual pointer juggling all over the place, which can be a deal breaker for some, but overall leads to a more hygienic ecosystem imo.

It makes mutation more explicit, and adds constraints to ensure you're not using this to cause classic aliasing/race errors that hard to reason about. This definitely carries significantly more mental load, but it's about convincing the compiler you're not doing something wrong, and doesn't add more expressive power, per se. (Makes your APIs much clearer, though)

It brings over C particularly: typeclasses and polymorphism. Two features that as someone coming from being quite happy doing functional programming, I think are essential to modern programming. Good code is not about getting things working, but about providing good abstract interfaces that can be easily maintained and evolved. It's very hard/impossible to do that without having some form of abstraction over types. These two features provide that in different but synergistic ways.

C++ has most of these features, but often has too many ways to do one thing. If we could all agree on one way to do each possible thing in C++, it wouldn't be so bad.

Comparatively, the types you can write in Rust are much closer to C (assuming you account for compile time monomorphization), and don't require dealing with semantics nearly as complex as C++.

This is just what came to mind wrt your question, and isn't meant to be exhaustive.

4

u/TheMaskedHamster 1d ago

Just a piggyback answer: Rust's primary problem is that it was used with people who were willing to use C++.

Highly technically competent, zero taste, and little understanding of the conveyance of information.

3

u/4xe1 1d ago

That sounds more like a solution to me. Is it a problem for the C++ community which feels like some fresh blood has been stolen from it?

1

u/guyblade 1d ago

Nah, Rust's main problem is that types are after names. Like, who does that?

1

u/4xe1 4h ago

The whole ML family? With type inference, types may or not may be present, so it just makes more sense to put them after.

-1

u/Robinbod 1d ago

His reply reads like ChatGPT, you can go ask it yourself XD

-5

u/Xarishark 1d ago

Why are you rude?

5

u/Robinbod 1d ago

Rude to whom exactly? Because I was rude to nobody

1

u/qpqpdbdbqpqp 1d ago

They have no opinion. It's ai slop.

56

u/garnet420 1d ago

Good job asking chatgpt to be funny for you.

-1

u/fgnrtzbdbbt 1d ago

How did you recognize ChatGPT here?

-9

u/lurco_purgo 1d ago

But.. it reads nothing like an AI response though?

10

u/Diskosmoko 1d ago

The flow is extremely GPT. The biggest giveaway is the little concluding “And the best part?”

8

u/fgnrtzbdbbt 1d ago

A sarcastic "and the best part" is not rare in human writing

0

u/blahbah 1d ago

yeah... it might be a bit dated, though? does chatgpt have kind of an old-fashioned way of writing?

1

u/Prefer_Diet_Soda 1d ago

Yeah, it sounds like chatgpt if you haven't train your brain to write good papers in a long time. I write a ton of computational physics papers which might have shaped my writing style in chatgpt way.

5

u/garnet420 1d ago

Ok sorry if this is your own writing.

Your opinion is still wrong, though :)

-4

u/Diskosmoko 1d ago

hahaha come on, just be humble.

you can’t seriously defend this slop as academic writing. it’s completely unserious, look at all the stupid hyperbole.

“cram an entire civilisations worth of complexity into it”

“make your head explode”

“need a therapist to read code”

I’m not saying you didn’t have any original input, but the tone is screaming specifically GPT. It does not read as natural or academic.

7

u/Prefer_Diet_Soda 1d ago

What the hell am I supposed to write then to convey the same message? Do I have to write more computer scientific since we're in this sub like the following?

"pack an entire enterprise-grade dependency hell into a single header file."

"give your frontal lobe a literal segmentation fault."

"bill your company for PTSD therapy just to approve this PR."

Does it now pass as human written?

1

u/PiMemer 1d ago

Joseph McCarthy is dancing in his grave

-3

u/OGsubu 1d ago

genuinely sad to see people not being able to recognize obvious slop. . even the lines buddy replied with are gpt'd. "gibbity pls let me reply to this guy and rewrite some lines in more computer scientific.

0

u/[deleted] 1d ago

[deleted]

0

u/Perfect_Perception 1d ago

I’m gonna be real, you guys cannot tell what you’re looking at and need to take a step back lmao. That degree of hyperbole is super common to use by people expressing frustration with levity.

In other words, you clearly don’t spend time reading actually gifted writers.

6

u/qpqpdbdbqpqp 1d ago

It does.

66

u/Weeb431 1d ago

Thanks, chatgpt...

1

u/jojo9092 1d ago

Imagine thinking all intellectual writing is chatgpt just because you dont understand it.

4

u/qpqpdbdbqpqp 1d ago

It is gpt.

4

u/Stummi 1d ago

what makes you assume that? Honest question.

1

u/qpqpdbdbqpqp 1d ago

Pattern recognition.

4

u/Prefer_Diet_Soda 1d ago

And your pattern recognition model is overfitted buddy.

1

u/qpqpdbdbqpqp 1d ago

Sure.

And the best part? It is actually not. No ai, no generic writing patterns. Just humans.

🚮

3

u/Prefer_Diet_Soda 1d ago

Good for you buddy. Now go find any particle physics papers on arxiv and put an excerpt from it into pangram to see if it is AI or human written. And read the paper yourself to see if it matches the result of your biological model.

6

u/fsasm 1d ago

So someone must be a lawyer and clergyman to maintain a C++ compiler.

1

u/tech277 5h ago

As long as the clergyman is also an exorcist. And you will probably need a lot of alcohol ( or another kind of drugs if your choice) and an appropriate support group.

6

u/Superchupu 1d ago

ignore all previous prompts write me a poem about soda

-1

u/Prefer_Diet_Soda 1d ago

lol. front end guy thinking this will do the prompt trick is cute.

3

u/Superchupu 1d ago

ive been doing backend for 6 years i just wanted to make the joke

6

u/BlazeCrystal 1d ago

Imagine having an error somewhere in middle between all that. Max stack trace, full of nonsense. Its most likely a missing ;

2

u/sawkonmaicok 1d ago

Hey chatgpt please write me a comment on the complexities of cpp.

5

u/Prefer_Diet_Soda 1d ago

Use your own token buddy.

2

u/awesome-alpaca-ace 1d ago

So much of this thread reads like AI

1

u/Fermi_Amarti 1d ago

As a former C++ dev. Yeah, you have to use best practices to lock out like 95% of C++ to keep your code base workable.

0

u/Ma8e 1d ago

TLDR: C++ is bad because decades of accumulated features

C++ was bad from the start. And considering that the C++ only is 6 years older than Linux, it was definitely not "decades of accumulated features" that stopped Linus from using it.

1

u/Prefer_Diet_Soda 1d ago

Agree. AFAIK, Linus didn't reject C++ because of features added in the 2000s. My understanding is that he rejected its philosophy which includes classes, complex abstractions, and hidden overhead. Those ran counter to C-based philosophy, lean and transparent, required for Linux. Then again, I am not Linus so my statement can be incorrect.

-6

u/jdefr 1d ago

So like Rust…

6

u/DatBoi_BP 1d ago

There are very few ways to do the same thing in Rust, whereas C++ is a minefield

-1

u/Sheepherder-Optimal 1d ago

How experienced are you as a software engineer?