Live data from Hacker News

Rethinking C++: Architecture, Concepts, and Responsibility

blogs.embarcadero.com

71–80 of 80 posts

Re: Rethinking C++: Architecture, Concepts, and Responsibility

#71

Earlier quoted context omitted.

I've read it. If you have a response then say what you're trying to say.

My criticism is not of any particular feature in C++ (though there is much to both hate and love about it), but of a particular approach to software engineering that the language design leans into, and which even the newest iterations of the language do nothing to alleviate, and in many many cases even reinforce. The easiest path in C++ is almost always the dangerous path. (The classic example is operator[] versus at…

Read my comment above: hardening is in, an effort to classify and remove UB is on the way. Implicit contracts (yes, that means basically to bounds-check even native arrays automatically) is on the way.

If you use warnings as errors they catch even subsets of dangling nowadays. Other ways of dangling have been made illegal (temporary conversions and range for lifetime extension).

I agree with you the defaults are still not the best but it is dteafily getting better.

But I think in the next years things are going to be tightened further in standard terms for better defaults. In fact, it is already happening.

The difficult part I think it is lifetimes.

Re: Rethinking C++: Architecture, Concepts, and Responsibility

#72

It's okay to admit when C++ doesn't have a feature. std::variant is an approximation of sum type support. It's ergonomics are an absolute travesty as a result, any errors you get are going to be 5 pages of template gunk, and I'm sure that using it pervasively is terrible for compile times. It has been possible to construct a type safe unit library as demonstrated in the article for forever. I've never seen anyone use…

Well, it is bad to have error() do UB.

It was slightly improved in C++26 under hardening:

https://en.cppreference.com/w/cpp/utility/expected/error.htm...

Re: Rethinking C++: Architecture, Concepts, and Responsibility

#73
post #3

I get the feeling author would just like to use a better language, like F# or Ocaml, and completely misses the point what makes C++ valuable. C++ is valuable, because the existing tooling enables you to optimize the runtime peformance of a program (usually you end up with figuring out the best memory layout and utilization). C++ is valuable becaus it's industry support guarantees code bases live for decades _without…

Better language? Well, now mix those with C libraries thst you need and make them generate code as efficient as C++ (I would assume people use C++ for a performance advantage of some kind in many scenarios).

Re: Rethinking C++: Architecture, Concepts, and Responsibility

#74
post #42

From TFA: > C++ is often described as complex, hard to learn, and unsafe. That reputation is undeserved. The language itself is not unsafe. On the contrary: it is precise, honest, and consistent. What is unsafe is how it is used if it is misunderstood or if one remains in old patterns. I think this take needs to stop. It’s a longer way to say “skill issue”. Meanwhile, decades of industry experience have shown that th…

The author seems to be writing about a dream language that isn’t actually C++. For example: > In my streams I showed how RAII can thereby also be applied to database operations: a connection exists as long as it is in scope. Only if that connection object doesn’t support move — we’re 12 years of C++ standards past the arrival of move, and it still leaves its source in an indeterminate state. > With std::variant, C++…

> Only if that connection object doesn’t support move — we’re 12 years of C++ standards past the arrival of move, and it still leaves its source in an indeterminate state.

I haven’t watched the streams he referred to, but… I am fairly certain the language itself says no such thing. You may be thinking of the standard library, which states that certain classes of moved-from objects have unspecified state. If you’re writing your own DB connection class, you can define moves to leave the object in whatever state you prefer, or disallow moves.

Admittedly it’s still a weird example IMO, because external factors can sever the connection while the object is in scope.

Re: Rethinking C++: Architecture, Concepts, and Responsibility

#75

Earlier quoted context omitted.

My criticism is not of any particular feature in C++ (though there is much to both hate and love about it), but of a particular approach to software engineering that the language design leans into, and which even the newest iterations of the language do nothing to alleviate, and in many many cases even reinforce. The easiest path in C++ is almost always the dangerous path. (The classic example is operator[] versus at…

Read my comment above: hardening is in, an effort to classify and remove UB is on the way. Implicit contracts (yes, that means basically to bounds-check even native arrays automatically) is on the way. If you use warnings as errors they catch even subsets of dangling nowadays. Other ways of dangling have been made illegal (temporary conversions and range for lifetime extension). I agree with you the defaults are stil…

All of that is great, and I strongly support these initiatives, but they are all teeny tiny bandaids.

Lifetimes are a crucial aspect of writing code in C++, yet they do not appear anywhere in the syntax. The same goes for synchronization. These problems are fundamentally unfixable without major, incompatible language changes.

Re: Rethinking C++: Architecture, Concepts, and Responsibility

#76

Earlier quoted context omitted.

The c++ features that get bolted on to replicate those in other languages tend to never reach parity because of all the legacy baggage they need to design around. Modules are not nearly as useful as one would hope. std::variant and std::optional are not nearly as ergonomic or safe to use as rust equivalents. coroutines are not exactly what anyone really wanted. If you're simply looking for checkboxes on features then…

> std::variant and std::optional are not nearly as ergonomic or safe to use as rust equivalents. > but I also use rust daily and you cannot really make a straight faced argument that c++ is catching up. I mostly use std::ranges, lambdas, and concepts, and I see them catching up, as an evolutionary process rather than a fixed implementation in the current standard. Nowadays I can do familiar folds and parallel travers…

Everyone is welcome to their own opinions and there is definitely movement in the right direction. However, it's a far cry from catching up. std: variant doesn't force you to check the tag and doesn't have any easy way to exhaustively match on all types it stores. I'm not sure I understand what you're comparing it to I'm terms of tuples. Forcing everything to be "nullable" or have a default state can be painful to deal with and introduces invariants I often wish were impossible. Ranges are definitely nice compared to what we had before. I'm probably just not used to them, but they aren't always intuitive for me and spelling them is very verbose. It's not nearly as simple as calling map and filter on a collection or iterator.

Re: Rethinking C++: Architecture, Concepts, and Responsibility

#77
post #74
post #42

Earlier quoted context omitted.

The author seems to be writing about a dream language that isn’t actually C++. For example: > In my streams I showed how RAII can thereby also be applied to database operations: a connection exists as long as it is in scope. Only if that connection object doesn’t support move — we’re 12 years of C++ standards past the arrival of move, and it still leaves its source in an indeterminate state. > With std::variant, C++…

> Only if that connection object doesn’t support move — we’re 12 years of C++ standards past the arrival of move, and it still leaves its source in an indeterminate state. I haven’t watched the streams he referred to, but… I am fairly certain the language itself says no such thing. You may be thinking of the standard library, which states that certain classes of moved-from objects have unspecified state. If you’re wr…

There is literally no correct way to handle move in an RAII context that doesn’t either (a) behave unexpectedly if you try to use the moved-from object or (b) permit a null value of the object. This isn’t a library problem — it’s a language problem.

Re: Rethinking C++: Architecture, Concepts, and Responsibility

#78
post #77
post #74

Earlier quoted context omitted.

> Only if that connection object doesn’t support move — we’re 12 years of C++ standards past the arrival of move, and it still leaves its source in an indeterminate state. I haven’t watched the streams he referred to, but… I am fairly certain the language itself says no such thing. You may be thinking of the standard library, which states that certain classes of moved-from objects have unspecified state. If you’re wr…

There is literally no correct way to handle move in an RAII context that doesn’t either (a) behave unexpectedly if you try to use the moved-from object or (b) permit a null value of the object. This isn’t a library problem — it’s a language problem.

What if the moved-from DB object lazily opens a new connection, if someone uses it again? Maybe that’s a null object, but at least the nullness isn’t really observable to the API user. Even the extra latency or possibility of failing to connect must be expected at any time from query() etc. so it changes little.

Also, I would say nothing is “unexpected” behavior if you document it and implement accordingly. And at least for this DB case, handling it is not onerous or stretching the idea of class invariants beyond usability.

I’m probably like what GP said, “high on the feeling of having finally grokked C++, which is no small feat.” But I either want to understand better why move is broken, or we can agree that things like move require too much skill to get right and there are better alternative languages.

Re: Rethinking C++: Architecture, Concepts, and Responsibility

#80
post #78
post #77

Earlier quoted context omitted.

There is literally no correct way to handle move in an RAII context that doesn’t either (a) behave unexpectedly if you try to use the moved-from object or (b) permit a null value of the object. This isn’t a library problem — it’s a language problem.

What if the moved-from DB object lazily opens a new connection, if someone uses it again? Maybe that’s a null object, but at least the nullness isn’t really observable to the API user. Even the extra latency or possibility of failing to connect must be expected at any time from query() etc. so it changes little. Also, I would say nothing is “unexpected” behavior if you document it and implement accordingly. And at le…

> What if the moved-from DB object lazily opens a new connection, if someone uses it again?

Great, so now the stateful settings on my database connection change depending on whether I move from it.

Database connections are kind of a bad example — having a connection drop is not really unexpected behavior, and a program that uses a database should be prepared for a connection to drop, so there’s kind of an invalid state on a connection anyway. But things like file handles or mutex guards aren’t like this — it’s reasonable to expect that, on a functioning system, a file handle won’t go away. And if I’m using a type-safe language that supports RAII, I would like the compiler to ensure that I can’t use an object that isn’t in a valid state.

Rust can do this, as can lots of languages that support “affine” types (that name is absurd). GC languages can kind of do this too, as long as cloning the reference is considered valid. Languages with “linear” types can even statically guarantee that I don’t forget to close my object.

C++ can ensure that an object is valid if it’s in scope, but only if that object is not movable, so “consume” operations are not possible in a type-safe way.

Post reply on HN