Cppfront, Herb Sutter's proposal for a new C++ syntax
github.com
github.com
What I find a bit strange is that he explains that he's been working on this since 2015 (with "most of the 'syntax 2' design work in 2015-16") and he doesn't want to give documentation because "that would imply this project is intended for others to use — if it someday becomes ready for that, I'll post more docs."
Why this reticence to opening the project and have others play with it and, potentially, contribute to it? I can't imagine a new language becoming successful with this mindset.
On this kind of project, it's more than syntax-deep. Documentation would have a lot of reasoning and justification in it, which can take deep work to keep up to date.
Aka, why argue about feature A, when the goal and long game is how A+B+C+D works?
This seems to be just a syntax frontend for C++. The underlying semantics stay the same.
BTW, if you dropped C++ a decade ago, you should now look into the modern improvements (C++20).
Fair. Still, the more recent C++ improvements tend to roll out slowly across the ecosystem and work environments.
- just the ability of doing
if constexpr(requires { T::some_member; }) { ... }
to check if a type has a member variable / function / whatever makes code infinitely clearer than the previous mess requiring overloads.- coroutines finally enable to properly abstract the data structures used by a type's implementation, e.g. you don't need to spill out anymore that your type stores stuff in std::vector or std::array or boost::small_vector or std::list etc etc to client code, and they simplify async code very well. for instance with Qt: https://qcoro.dvratil.cz/reference/core/qprocess/#examples
- three-way comparison and automtic generation of comparison / equality function is really great for removing needless boilerplate
- void f(auto arg) { ... } instead of template<typename T> void f(T arg) { ... }
- foo{.designated = 123, .init = "456" }; (although it was already more or less supported on every relevant compiler for years
There is a way, but it's cursed:
C# is going the route of allowing required/optional fields in designated initializers, and from my point of view it is just a mess for what should be a constructor call.
C++ 20 gets a format feature that's basically a weaker {fmt} library but as part of the standard library. A string formatting feature with reasonable performance and safety as you might be used to from other modern languages.
Concepts is nice, that's basically way to express duck typing, it is often described like you're getting more than duck typing but that's all you actually get - but hey, duck typing is useful, and Concepts should have decent error messages whereas doing the equivalent with SFINAE is a recipe for awful diagnostic output.
In fact, C++ Concepts can be used for early enforcement of compile-time duck typing, just for better error messages. In the Standard library they are commonly used that way, for backward compatibility.
But Concepts can also implement named-property matching. It is entirely up to the designer of a library how much of each to present.
You have repeatedly (across numerous HN threads) insisted that Concepts aren't just duck typing but that doesn't make it so.
> But Concepts can also implement named-property matching.
That's still just duck typing (and it's awkward to do properly). Contrast with the (eventually abandoned) C++ 0x Concepts, which actually has semantic weight to it. Concept Maps allow C++ 0x Concepts to have some sort of chance in a language that's already in widespread use, because you can adapt an existing type to the Concept using a Map.
But C++ 20 Concepts doesn't offer any of that, after about a decade it's just duck typing.
Concepts are just the equivalent of if instanceof.
1) malloc/new & free/delete are now solidly legacy territory of the "unless it's a placement `new`, you're likely doing it wrong". make_unique && make_shared all day long.
2) templates that are understandable by mortals thanks to `if constexpr` instead of SFINAE nightmares.
3) static_asserts
4) lambdas (which is going to get way more useful with https://en.cppreference.com/w/cpp/utility/functional/move_on... )
5) std::format
6) attributes like [[nodiscard]] being standard
7) std::move making passing std:vector & similar containers around not being terrifying (this is what also really helps #1 be possible)
I'm sure I missed some stuff, but I reach for all of those regularly.
Move-only values are very used for common situations of unique resources that should not be duplicated.
But, what if you want to enclose a move-only value in a std::function? Are you simply out of luck and must give up on your dreams? A move-only function lets you get that work done.
This is awfully wrong. C++ might be convenient to express ownership and manage object lifetimes, but they are not the only way to express ownership by far.
Take for instance Qt, which relies heavily on new-ing up objects still up to this day, as it has its own ownership and object lifetime management system.
New/delete are still basically deprecated territory. Qt isn't any different here, other than it seems they are behind the curve with make() variants of their pointer containers. So you'd want to make your own of that, and then go back to the world of "new/delete are deprecated"
2) in light of that your comment is unintentionally hilarious. C++ became a syntax swamp 15 years ago and it is getting worse every release. I anticipate it getting worse as rust, a hilarious syntax soup in its own right, continues to march forward.
That is very much not true. In fact the syntax simplification is of less interest to me than the clarification/simplification of semantics. Most of the dangerous / confusing parts of c++ come from the necessary c compatibility.
So for example, you don’t have to worry about the bug-inducing C integer promotion rules.
I heard this recently: open projects are like Good Will Hunting but in reverse. You start as a respected genius, and end up as a janitor getting into fights.
"Cppfront is a personal experimental compiler from an experimental C++ 'syntax 2' to today's 'syntax 1,' to learn some things, prove out some concepts, and share some ideas."
- Transpiling to C++ means it will work wherever C++ works. On any toolchain. With relatively little effort. It shouldn't be hard to add CMake and bazel support, for instance. Even mostly dumb makefiles should be workable.
- Approaching the community this way could avoid fracturing the community more than it already is.
- Targeting specific success metrics (like CVE causes) could provide focus around design and requirements
- Seems simpler and easier to learn and teach.
Cons:
- The pitch assumes C++ modules are implemented, ready to use, and reasonable accessible.
- There will be a lot of tooling to reinvent: static analyzers, syntax highlighters, etc.
- At least for a while, debugging cpp2 problems will involve juggling multiple parallel realities: cpp2 code, C++ code, and actual program behavior (i.e. compiled binaries).
- Doesn't address other major C++ ecosystem problems like build times, packaging, build systems, etc. cpp2 could make them slightly worse.
He also demos error messages, and was able to get errors from a cpp compiler (msvc) that point to his cpp2 file with a line number and readable error.
The main reason to use it in production is that it's not meant to be used in production and makes no attempt to be suitable for production use or avoid breaking changes, so the stuff in your cons list doesn't really apply. It's kind of like looking at someone's scale model of a proposed city layout and say that a pro is that tiny plastic houses are cheap to manufacture at scale but a con is that the houses may be too small to live in.
But all of your points will become relevant if cppfront eventually gets to a state where it's a serious transpiler meant to be used in production.
Besides, the C++ community seems to overlap with academia by way of its feature updates, reveling in complexity (e.g. "C++20 ranges are totally awesome: memory-safe and performant iterators!").
I will say that it's impressive how much of this has already made it into C++ language specs. But given that Sutter has chosen to release this now, I get the sense that of what remains, large portions cannot continue to simply be done that way.
It also means you don't really benefit from any of the new semantics. Why do all of that static analysis work to establish safety, and then throw the results away by writing out clunky old C++? It all makes very little sense.
It's a bit more complicated than, say, typescript to JavaScript, but in spirit is not so very different, right?
- In general, "unsafe" code inside a module may depend on invariants maintained by "safe" code in the same module. For example, the "length" field in a Vec can be changed by the safe internals of Vec. But if the safe code in Vec sets an invalid "length" value, then unsafe code relying on "length" might fail. So once you find an unsafe block, you may need to audit some of the surrounding safe code in the same module.
- Unsafe Rust is actually slightly less forgiving than C or C++, partly because Rust is allowed to set "noalias" on lots of immutable references, IIRC. The Rustonomicon talks a lot about issues like this.
As far as support in compiled libraries is concerned, that would depend on how those features are implemented in C++23 anyway.
I assume that as long as the cpp2 syntax is clean, you could improve on compile times. In theory, cpp2 files could compile much more quickly that C++, so as you migrate code to cpp2 your builds speed up?
It all depends on how well Sutter has thought through the cpp2 language design I suppose.
Big reasons people look for non-C++ solutions include better build times, dependency management, etc.
- https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p14...
- https://vector-of-bool.github.io/2019/01/27/modules-doa.html
Are there any notable new updates related to this controversy?
Only if you only use this Cpp2 thing. Because the C++ you write may not play nice with the C++ this transpiles into.
This approach is similar to Rust's editions. IIUC, nearly all of the differences been editions are compartmentalized in the frontend, deprecating syntax and rewriting old edition code to rustc's current IR.
Here's the guide explaining the most significant hack:
https://doc.rust-lang.org/edition-guide/rust-2021/IntoIterat...
There was a proposal to do Editions for C++, Epochs, https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p18... but it was not accepted.
Can you explain?
The array type doesn't have an iter() method. It will coerce into a slice, which does have an iter() method, but doesn't care whether it's an array we're slicing.
Imagine writing something using OpenSSL, it might look kind of weird or breaking the flow of the new syntax, just because of its legacy.
This kind of claim is extremely common HN but utterly foreign as someone who actually writes C++ for a living.
[1] https://www.theregister.com/2003/06/09/sun_preps_500m_java_b...
Adding JDBC and later nio is probably what got it there the most.
Congratulations, this is the stupidest thing I have read this week.
Of course the 20 years of enormous popularity and huge success in multiple industries must be due to marketing. I mean, what else.
It was the initial push (with gobs of money) given by Sun that gave Java the momentum. No language was ever pushed so hard in the marketplace by any other organization. Without that push, the language would never have the popularity that it enjoys today. There is nothing inherently "superior" to the Java language. Far better languages have fallen into obscurity because they did not get the publicity (eg. Eiffel) that Java did.
I think everybody does this and it's totally ok. Nobody needs all of C++.
I guess that's why I like simpler languages more, where I don't have to think about how the language or compiler is going to treat my code.
Go is OK. It's a little faster to get a first version working, but the result tends to be slower and more likely to crash on memory/concurrency issues than a rust version would be.
I use both; go is fine for spaghetti ~architecting~ devops-ing piles of microservices together. Rust is much better for the data path though.
I spent the last two months coding in Go pretty much not enjoying it. Nil is not always equal to nil in third party libraries through an interface -- and the compiler wouldn't warn about it.
serde is also world class for serializing/deserializing, and can generate implementations for you based on struct definitions independent of data format (json/bson/yaml/toml/etc).
The only sharp edge you may bump into for a web service is in your choice to go either sync or async, and if you go async you must then choose a runtime (usually tokio) as these are libraries and not integrated into the std lib beyond just the `async` keyword and a few traits.
But yeah, serialization in Rust is a breeze compared to Go. Probably one of the things I hated the most, along with the errnil boilerplate.
What were your issues with the C FFI? That usually gets praise from people.
Not type safety so much as program correctness. Type safety is just one aspect of it. Golang is "type safe" but still panics on null pointer accesses.
There's certainly a large learning gap; both for "classic" C++ as well as the popular modern features. But once you get over that I haven't found other peoples C++ any harder to read than other peoples C or JavaScript or Python.
No one's reaching to esoteric features like "..." or atomics unless they're some low level library wizard (in which case all their friends are also low level library wizards) or the situation actually calls for it.
Absolutely! C# introduced "var" back in 2008, and to this day there are people arguing that you shouldn't use it except when it's outright impossible to spell out the type.
From what I hear, it's the same story with "var" in Java 10.
I also don't think they're arguing cxx98 is a good language, especially by today's standards.
The argument they're making is that the tact C++ has taken in more modern revisions is actually _worse_ than writing code the shitty 1998 way. At least for axes games care about.
You mentioned move semantics, which is a great example. For a lot of game/engine code, move semantics are just adding a pile of complexity for negligible gain. Ie. Why not just use a pointer?
They didn’t have any argument. Just old man yelling at cloud.
> You mentioned move semantics, which is a great example. For a lot of game/engine code, move semantics are just adding a pile of complexity for negligible gain. Ie. Why not just use a pointer?
Because it is error prone.
OP said:
>> I've been engineering videogames in C++ for almost 20years working on AAA games
It seems like you're implying that every single business domain uses C++ the same way that your business domain does. Instead of insinuating OP is a common HN commenter that doesn't write C++ for a living, which seems to be an incorrect assumption, maybe you should recognize that not every business domain uses the same subset of C++ as you?
Your comment just reads as an arrogant opinion hiding behind a false sense of authority. It sounds like you're saying, "anyone that disagrees with my opinion must be some hobbyist programmer that doesn't know what a real programmer that does this for a living actually codes like". As if coding in a language for your job automatically implies that the code will be of a higher quality than hobbyist code. Its gatekeepy and gross imo.
I've worked professionally with disgusting C++11 code that was an amalgamation of code from some people that clearly knew what they were doing, and a lot of code that seemed to be from people that had no clue what was really happening. They had no clue because of all the hidden complexities in modern C++ that Herb Stutter is trying to reduce.
This should be a huge klaxon for the C++ community. Whether or not it's true, it's a massive problem for the C++ one way or the other.
>This kind of claim is extremely common HN but utterly foreign as someone who actually writes C++ for a living.
The above comment is equally applicable to their comment.
One could see why it's more important for a game dev to have a language that forces your mental models to more closely match what the hardware is actually doing, rather than using abstractions that make it less likely that you'll introduce bugs.
You are voicing something that was true very long time ago. In world where you must be cross play cross platform to cover enough players there is no room for "what the hardware is actually doing". There only few games made by platform holders with exclusivity in mind are still there.
If I want to write fairly performant code quickly I'll reach for C++ and use more of the toolbox.
How would you implement something like unique_ptr without move semantics?
Can you give an example of angles being used to avoid needing move semantics?
I can't imagine writing a long running, memory conscious, and fast C++ program that uses whatever 'macro' management strategy you envision.
We also happen to do 0 other steady-state syscalls (not just 0 mmap, munmap, mremap, etc.), so we can just run the program under valgrind and any time valgrind prints something is a bug.
We didn't use C++ but some other software I've heard uses a similar approach is written in C++.
So I also don't see how these are related
The typical pattern is to create an arena whose lifetime is the lifetime of some important operation in your program (say, serving a single request or processing a single frame), do all of the logic of that operation via simple allocation in the arena (ideally bump-pointer allocation), don't free/delete anything at all, then when the operation is finished, just free the arena itself (or don't, and reuse it for the next operation by just resetting the pointer).
This implies, or at least allows, a very different style of writing C++ than usual - no need for tracking ownership, so no RAII, so no constructors or destructors, so no smart pointers.
Of course, you can also write this kind of code with RAII and smart pointers (with nop destructors for non-resource objects), using placement new to construct objects inside the arena and so on. But it's not necessary, and some find it easier to avoid it.
A std::string is an example we can use, it acts fairly similar to unique_ptr. Sometimes you just need to heap allocate something, work with it and/or store it as a member, maybe pass it to a function or whatever, and then have it cleaned up at the end of the scope or when the object that it's a member of is destroyed. I don't think an arena can replace this use case.
If we need something with multiple handles, that doesn't have a trivial and predictable lifetime (such as objects / variables in a programming language interpreter, or entities in a video game), you can reach for shared_ptr or an arena. In the specific example I gave I would certainly prefer the arena, and would implement a garbage collector to deallocate stuff when it became unreachable.
The arena pattern is fairly common in Rust, because in Rust even a reference counted smart pointer doesn't let you bypass the "shared ^ mutable" enforcement from the borrow checker! Having everything owned by the arena greatly simplifies issues with the borrowck, and you can just allow callers to take temporary references and drop them when they are finished with them. There is a crate called "SlotMap" that provides a data structure to assist with the pattern (there is a C++ SlotMap library as well somewhere).
Anyways I have rambled a bit, but I think unique_ptr solves a different problem than what you describe, which instead is more of an alternative to reference counted pointers (shared_ptr).
If you use a bare pointer instead, not only do you get the same guarantees, but now if you need to modify the code to actually share that object, there's no need to modify any references from unique_ptr to shared_ptr.
With an arena ideally you can just pass out actual references (T&) to callers instead of pointers!
Boost has a full implementation of boost::unique_ptr that is identical to std::unique_ptr but works with C++98 [1].
https://www.boost.org/doc/libs/1_80_0/doc/html/boost/movelib...
I guess this means that you don't have to have it at the language level to support unique_ptr, but you do need some form of move semantics at least. I think having it built into the language as a proper feature is preferable personally.
https://www.boost.org/doc/libs/1_80_0/doc/html/move/what_is_...
>Rvalue references are a major C++0x feature, enabling move semantics for C++ values. However, we don't need C++0x compilers to take advantage of move semanatics. Boost.Move emulates C++0x move semantics in C++03 compilers and allows writing portable code that works optimally in C++03 and C++0x compilers.
Much of the C++ code out there is still C++98 and a lot of the "so called problems" had already been solved by using established code patterns (not GoF). "Modern C++" has added a lot of complexity but not for much benefit; its fanboys always use hand-wavy phrases like "much nicer", "makes programming simple"(!) etc. which mean nothing. After the introduction of STL and explosion in the usage of "Generic Programming" the only thing the language needed was Concurrency support for Synchronous/Asynchronous programming. Instead the ISO committee went off the rails into "modernizing" the language to try to make it like Python/whatever. The result is that experienced C++ developers are very cautious about adopting "Modern C++" while the larger "know little/nothing" crowd are raucously running around making all sorts of unwarranted claims.
IMO, today the greatest enemy of the language is the ISO committee itself.
I can continue.
But to give a few concrete examples;
>Which “so called problems”?
Apparently "variants" were introduced to solve problems with POD "unions". Mere complexity for not much benefit. Nobody i have worked with ever said "unions" were difficult to use.
>Which “established code patterns?”
RAII as a solution to People harping about memory problems. I have written entire protocol message libraries just using RAII to avoid memory leaks.
>Why “it means nothing” and to whom?
When "Modern C++" proponents say everything should be written with the new features using hand-wavy phrases, "it means nothing" to experienced programmers. If we find a feature useful to model a concept and/or express something we will use it; but always weighed against how complex it is to read and maintain. A good example is template meta-programming taken too far.
The key reason C++ took off was because it added minimal "Zero-Overhead abstraction" constructs over the baseline "low-level and simple" C core. Suddenly programmers could have their cake and eat it too. The evolution of C++ should have continued in the same spirit but instead in a misguided effort to compete with later and differently designed languages a lot of complexity has been added for not much apparent benefit.
I wouldn't trust anyone to write the correct boilerplate for
union { std::string s; int v; };
unless they'd do just this all day longno it's not. it's fine to have non-POD unions - but you have to be careful to call constructors and destructs of non-POD's explicitly. thus, variant which automates that.
Also, I didn't talk specifically about unions, but about sum types. There is a need for saying that an object X can be of type A OR type B, no matter the properties of these types.
Only from C++11 (or is it later?). So a problem was created by relaxing the existing requirements for a "union" to which a solution was proposed by adding a "variant"? Something which had no runtime overhead (but UB) now has runtime overheads.
Regarding your point about "sum types", agreed.
I'm just pointing out that you didn't have anything concrete in your messages.
> Apparently "variants" were introduced to solve problems with POD "unions". Mere complexity for not much benefit. Nobody i have worked with ever said "unions" were difficult to use.
https://stackoverflow.com/a/42082456
First link in Google, how is moving from undefined behavior and not needing manually declare type of union (sic!) is a "mere complexity for not much benefit"?
> Nobody i have worked with ever said "unions" were difficult to use.
Sure, enough people say that about for and while loops while avoiding functional patterns like a plague.
> RAII as a solution to People harping about memory problems. I have written entire protocol message libraries just using RAII to avoid memory leaks.
Which "modern, overhead features" are solved by RAII?
> When "Modern C++" proponents say everything should be written with the new features using hand-wavy phrases, "it means nothing" to experienced programmers. If we find a feature useful to model a concept and/or express something we will use it; but always weighed against how complex it is to read and maintain. A good example is template meta-programming taken too far.
Who are these experienced programmers and how do you define those? Your circle of people?
> If we find a feature useful to model a concept and/or express something we will use it; but always weighed against how complex it is to read and maintain.
Modern features are literally easier and less complex than old way. Like in the variant example.
> A good example is template meta-programming taken too far.
And how often "modern c++ proponents" suggest meta-programming to solve ordinary problems? Because everywhere I've encountered C++ discussions, those were resorted either for internal usage or suggested to avoid at all.
> The key reason C++ took off was because it added minimal "Zero-Overhead abstraction" constructs over the baseline "low-level and simple" C core.
There's no key reason C++ took off. It took off, because it took off.
> The evolution of C++ should have continued in the same spirit but instead in a misguided effort to compete with later and differently designed languages a lot of complexity has been added for not much apparent benefit.
There's a reason why those differently designed languages were defined differently. Maybe instead of blindly hating evolution, try understanding the reasons behind it.
>First link in Google, how is moving from undefined behavior and not needing manually declare type of union (sic!) is a "mere complexity for not much benefit"?
Because you have not understood the definition and guarantees of a "union"; and UB is not always a bad thing. "union" is explicitly defined to be a POD with all it entails. The stackoverflow answer does not provide anything new. If you want something more, you code it explicitly when needed. No need to burden the language; in fact it creates more problems because "variant" does not guarantee layout compatibility.
>Sure, enough people say that about for and while loops while avoiding functional patterns like a plague.
Of course familiarity and clarity always trumps "new patterns".
>Which "modern, overhead features" are solved by RAII?
The harping on "never use naked pointers" in your code.
>Who are these experienced programmers and how do you define those? Your circle of people?
Of course; It should be the same for you and everybody else too!
>Modern features are literally easier and less complex than old way. Like in the variant example.
This is what we are debating; it is not a fact that you seem to assume.
>There's no key reason C++ took off. It took off, because it took off.
There is always a tipping point. In C++'s case it was compatibility with C and new language constructs for higher level abstractions.
>There's a reason why those differently designed languages were defined differently.
Exactly; Each starts with a Computation Model and evolves a syntax to that model. The evolution should not be willy-nilly dumping everything and the kitchen sink into a language. C++98 was complicated enough but still manageable but what has happened from C++11 onwards is just too much complexity requiring even more effort from experienced programmers. You cannot hand-wave it away by saying "Modern C++ is a whole new language so forget baseline C/C++ cores" which is quite silly.
You can do this without variants by manually defining your own unions and managing the tag yourself, but this is very not type safe and requires extra boilerplate code to manage!
Maybe you have never used and don't care about this feature but it's actually pretty useful! Tagged unions make representing certain types of data very elegant, for example nodes of an AST or objects in a dynamic programming language. You can use OOP patterns and dynamic dispatch to represent these things as well, but I think tagged unions are a better fit, and you get to eliminate a virtual method call by using std::visit or switching on the tag.
I suspect that maybe you have never been introduced to sum types in general which is not uncommon! I am curious if you have experience with using them or not?
I have already pointed out in some of my other replies why it is wrong to consider a variant as a replacement for POD union.
Technically you can implement it manually, but the same thing could be said about all language features, even in C. We don't need actually need structs built into the language, we can just allocate blocks of data and handle the offsets by hand. We don't need functions, we can just push stuff to the stack and use the call opcode. The same goes for various loop constructs, goto can do everything "for" and "while" can do!
I don't think "we used to roll our own X back then" is a strong argument for something being bad or unneeded. Abstractions are all about allowing the computer to do work for us, and making things less error prone and more expressive. This is why we have programming languages to begin with and don't write everything in assembly!
Language design is a fine balance; the addition of Abstraction Features has to be balanced against the cognitive load it imposes on the Programmer. These abstractions also need to be tailored to a computation model supported by the language. For example, the Niklaus Wirth school of language design was famous for insisting on minimalist languages; You only added features if they were "needed" to support the model of computation and all extraneous/trivial features were omitted. C++ took the opposite route from the beginning which was ok to a certain extent since it increased the expressive power of the language (eg. multi-paradigm). But over time the balance is being lost and the cost of cognitive load is outstripping the benefit of an added abstraction. This is bad and not welcome. So looked at in this light how many of the features in Modern C++ are essential and how many are extraneous (eg. Concurrency features = Essential, variants/syntactic annotations/etc. = Extraneous)? That is the fundamental issue.
Claiming that move semantics, structured bindings, lambdas, designated initializers, non-impliciy self, override for virtual functions marker, [[nodiscard]], coroutines, modules, smart pointers, safer bitcasting, non-ambiguous bytes (std::byte), scoped enums, delegated and inherited constructors, constexpr, consteval and much, much more are not improvements for day-to-day... well, shows me that you must have not used it that much.
Lots of good C++ based systems have been written before any of the above existed. That proves that they are not "needs" but merely "wants". Only some in the above list are worth adding to the language while others are just noise (as an example keywords like "override"/"default"/"nodiscard" just add to syntactic noise rather than any actual benefit). The members of the ISO C++ committee simply pushed through their pet "wants" for brownie points.
The fact is that you will have to check by hand or inspection what the compiler can tell you, right? That saves time and effort. Especially when refactoring.
Go refactor the signature 2 or 3 virtual functions with let us say a couple parameters each in two classes and 4 or 5 derived classes, one two levels deep (this is real code what I am talking about) and do it with and without override.
Try to measure the time it takes you with or without override keyword. You could use some refactoring tool, fair. But you do not have that available in every context.
This is just trivial and does not really support your arguments.
The way it was done before was to use the "virtual" prefix keyword for virtual functions across the entire class hierarchy. Merely a discipline which was enforced religiously via coding guidelines; it gave the programmer the needed cue while the compiler doesn't care. You then did a Search and Replace as needed.
Seems suspicious to me that you claim to have used C++ for long. You do not understand even how virtual functions worked in the language.
I said;
>to use the "virtual" prefix keyword for virtual functions across the entire class hierarchy.
What is meant is that the entire signature of the virtual function including the "virtual" keyword is reproduced in all derived classes thus ensuring that no mistakes are made.
I also said;
>it gave the programmer the needed cue while the compiler doesn't care
It was just good programming discipline rather than depending on compiler crutches.
Just to drive it home; here is an example: https://stackoverflow.com/questions/4895294/c-virtual-keywor...
No, that did not enforce safety or solved the refactoring problem I told you. It seems you do not want to listen that override fullfills the case where you assert that you are overriding (and it will be a compile error). You have a misunderstanding and a wrong mental model for how virtual worked. It did not enforce anything, just declared a virtual function, no matter it was new or an override. virtual can introduce a new virtual function by accident.
Example:
class MyClass {
public:
virtual void f(int) {}
};
class MyDerived : public MyClass {
public:
virtual void f(int) {}
};
Refactor: class MyClass {
public:
// NOTE: signature changed to double
virtual void f(double) = 0;
};
class MyDerived : public MyClass {
public:
// FORGOT TO REFACTOR!!! STILL COMPILES!!!
virtual void f(int) {}
//void f(int) override {} // COMPILE TIME ERROR!
};
> You are making inflammatory statements without understanding what has been written; it is not appreciatedNo, I was not. I was just showing you do not know how the mechanism works with facts.
Above you have an example of why what you say does not work. I would recommend to talk more concretely and not to make too broad statements about tools you seem to not know in detail but it is up to you, I would not stop you. Just a friendly recommendation ;)
btw - Anybody who has been following this chain of responses can see who is the one using inflammatory language to hide their lack of comprehension and knowledge.
For the last time;
1) We did not depend on compiler crutches to help us.
2) We enforced coding discipline religiously so that all virtual functions are unambiguously identified with full signatures across the entire class hierarchy.
3) When you need to refactor you did simple search/replace on the full signature using grep/ctags/cscope/whatever across the entire codebase.
That is all there is to it.
This might be the most valueless discussion i have had on HN and that too over a utter triviality...sigh.
> 3) When you need to refactor you did simple search/replace on the full signature using grep/ctags/cscope/whatever across the entire codebase.
That can break in a ton of ways. Still. For example, if the pattern is not correct for grep. ctags/cscope does not fully understand C++ also AFAIK. Then you are shifting from debug your code to debug your greps, etc.
Not sure how you did it exactly, but I see it as a fragile practice. Because you rely on absolute human discipline.
> This might be the most valueless discussion i have had on HN and that too over a utter triviality...sigh.
Sorry for that. Feel free to not reply. But do not take it to the emotional terrain so fast. I just argued since the start that C++ improvements are not an accumulation of "pet features" for the sake of it. It is you who presented that as facts without any kind of evidence in the first place.
To be clear, I do not care about C++ that much or anything particularly. But I have used it enough to identify one of your top posts as emotional and non-factual.
Greetings.
I should have probably said something like "override is just a compiler crutch for lazy programmers who can't be bothered to be careful in their job" but i thought it might get me banned from HN :-)
>It is you who presented that as facts without any kind of evidence in the first place.
My comments just in this whole thread refute your above statement.
>But I have used it enough to identify one of your top posts as emotional and non-factual.
I don't think you have understood the motivations behind the features you claim to have used, how they were solved earlier without those features and properly judging whether the change was worth it.
Of course good systems have been designed, but try to use the stl without lambdas. Or return big values by pointer bc u dnt have value semantics with all its associated usability problems. Try to write sfinae vs concepts or write a std::optional type without C++23 explicit self. Try to go and add boilerplate for free bc you did not have delegated constructors. Or try to build a table inside C++ at compile-time pre-constexpr. Do generic programming withou if constexpr. I have done myself all of that before C++11 and it was way more difficult to write a lot of code. Of course under your view anything is "nice to have". Then just grab assembly. But for the people like me that use it, I'd rather see it evolve with useful features.
Those keywords do NOT add noise. They add safety, since defaults cannot be changed. C++ is as good as it can get with its constraints. You can think it is bad, but C++ is an industrial language, real-world and with compatibility as a feature.
I can buy you could disagree with some of the decisions, but mostlyit fullfills well its purpose. Herb is just trying to create a C++ 2.0 that is simpler and 100% compatible. It is good, very good to not start from scratch if you are in industrial envs. You just do not throw away 40 years of code that has tested the pass of time pretty well, no matter it was written in C or C++.
I am a longtime programmer firmly in the C++ camp (as an example, see one of my earlier comments here: https://news.ycombinator.com/item?id=27854560). I am also not against the evolution of the language. But what I (and many other C++ programmers) are against is the messianic zeal of the "Modern C++" proponents hellbent on changing the language to be more "user-friendly" like Python/whatever in a mistaken belief that "C++ programming is now made simpler". By adding more and more features the interaction between them is now only more complicated then ever (i.e. When and How do you use them correctly and efficiently? How do you combine them into a clean design?) making the programmer's job that much more difficult (as an aside, this is also the reason beginning/new-to-C++ programmers give up on the language).
The above problem is compounded because C++ is already a multi-paradigm and multi-usage language i.e. situated in a co-ordinate plane with the above axes.
The paradigm axis:
- a) Procedural Programming i.e. "better C". - b) Object-Oriented Programming. - c) Generic Programming. - d) Compile-time Programming.
The Usage axis:
- a) Low-level Interface Programming eg. MCU programming. - b) Library Programming. - c) Application Programming.
Every feature ideally sits at an intersection of the above two axes. Thus for example, the "auto" keyword from C++11 is best suited for "Generic Programming paradigm" and "Library implementation usage" (eg. in the implementation/use of STL). But what is happening is that the "Modern C++" proponents are using it willy-nilly everywhere (because "hey i never declared any stinking types in <scripting language>") making the code that much harder to comprehend. Similar arguments can be raised against a lot of other features. This is the reason many of us are cautious w.r.t. the new features; we know the existing potholes, have worked around them and have our system under control and in hand. The ramifications of introduction of new features just for its own sake is unknown and makes us very nervous.
Of course you layer features on top of existing things. It is the only way to "simplify" the language and keep it compatible. For example, now you still have raw pointers: noone recommends managing raw memory anymore, the recommended thing is to return a smart pointer or do RAII inside your class (this last one pre-C++11).
How about virtual functions? Oh, now you have to know more (and this actually does not really hold true in every context, sometimes it is easier): of course you might need to know more! Recommendation is to mark with override! How about template metaprogramming with SFINAE! No! Hell, NO if you can avoid it! That is why constexpr, if constexpr and concepts exist!
It is not more difficult to program in the last standards, it is way easier to program. What is more difficult is that you need to know more. But you do not need to know absolutely every aspect of the language. They layered features that are more general so that you stop using the legacy ones, and that is an improvement inside the constraints of what C++ is allowed to do right now to keep it useful (compatibility).
If what you want is to learn to program in permutations of all styles, including the old ones, in C++ and in all kind of devices... I do not know people that are experts at all domains and levels of the stack, no matter the language you use. So again, you can use your subset.
> But what is happening is that the "Modern C++" proponents are using it willy-nilly everywhere (because "hey i never declared any stinking types in <scripting language>"). Similar arguments can be raised against a lot of other features.
You basically said nothing concrete above.
BTW, noone prevents you from using many of the inferior styles. And by inferior I do not mean non-modern, I do not buy modern for the sake of buying it, I use the features that make my life easier, and the same feature or library thing that makes my life easier in one context is the one I discard in another (for example dynamic memory in embedded or fancy ranges in videogames I might need to debug and understand to the vectorization loop level if I do not have the parallel algos).
You can still, as I told you in the comment above, ignore override, for example, and try the refactoring exercise I told you, making your life more difficult on your way.
Or ignore structured bindings in for loops and declare the variables yourself for pairs. Or you can do some fancy metaprogramming with SFINAE and spend 3 hours figuring out what is going on or why you cannot use it in the parameter type of an overloaded operator and have to use it in its return type or guess which overload you are selecting, because if there are two difficult things in C++ those are initialization and overloading.
In the meantime, some of us will be using some concepts to constraint the overload set, or, even better, present as interface the weakest of the concept fullfilling a function and use if constexpr inside to choose the optimized paths for each type as needed.
Those are improvements, BIG improvements on how I write everyday C++ code. The language is big, bc there is not a choice. But a good training and taste for what to use and what not to use in your area of work is necessary.
By the same argument, plenty of good code was written in C before C++ existed, so the entirety of C++ is "wants" rather than "needs".
A good example is Linus Torvalds opposition to the use of C++ in the Kernel.
I've been writing C++ code professionally for almost 20 years now, and Python for 10 years. I tried to think of any C++ features post C++98 that would make it "like Python", but I can't think of any. Can you give some specific examples?
It even states: C++ added tuples to the standard library in C++11. The proposal even mentions Python as an inspiration.
I have never worked on games -- maybe that domain has simpler internals -- but there are important categories of C++ software outside of games that unambiguously benefit immensely from the modern C++ features. In databases it is common to ride as close to the bleeding edge of modern C++ as is feasible because the utility of the new features is so high.
Whenever I move from the old-style C++98 code units to newer modern C++ ones I breath a sigh of relief. It's beautiful, safe and modern; it reads like poetry.
If I get a code review where someone manually calls new/delete I will fail that code review.
That said, I agree this proposed "new syntax" is ugly and unreadable and unnecessary.
I'm sorry but this assertion does not pass the smell test.
Sticking with C++11 means you do not have std::make_unique, and any claim that these "best software engineers" not only fail to use smart pointers but also refuse to even consider instantiating a std::unique_ptr in a safe, standard way and for no reason at all is something that lacks any credibility.
> Modern C++ is for experienced C++ developers
It really isn't. "Modern" C++ is just the same old C++ with useful features that improve the developer experience (see aggregate initialization, for starters,nested namespace definitions, utf8 character literals, structured binding, etc) and don't require developers to resort to in-house trickery passed around through tribal knowledge to implement basic features (move semantics, constexpr, etc).
> It's just disappointing...
Speak for yourself. It's fantastic that people continue to improve the language and make everyone's life easier, instead of being stuck in the C++98 mud. Each and every single major standard release since C++98 brought huge productivity and safety improvements that everyone stands to benefit. Even standardizing stuff from Boost and the like is a major step forward.
It's totally fine that you personally prefer to not benefit from any of the improvements that sprung in the past two decades, but don't presume for a minute that you represent anyone beyond yourself when making Luddite-like claims.
Pretty much every non-trivial C++ engine i've seen has its own equivalents for memory management. Even a game engine development book i bought in ~2001 (meaning it was written before then) had a chapter dedicated to implementing smart pointers.
They are there because it makes sense to have them. Now you don't have build and maintain your own ship.
That standard implementation is a thin wrapper over malloc(). That standard malloc() is not necessarily good enough. The performance is not great, but worst of all when you have many small objects, they are scattered all over address space. Chasing many random pointers is expensive.
While it’s technically possible to use custom memory management with std::unique_ptr with that that second template argument with custom deleter, it complicates things. The code often becomes both simpler and faster when using custom smart pointers instead.
That’s not specific to videogames, applies to all performance-critical C++. These standard smart pointers are good for use cases with a small count of large long-lived objects. For large count of small things, the overhead is too large, people usually do something else instead.
The operative word is "it enough".
Let's not fool ourselves by claiming that all memory allocations take place in hot paths, and that all conceivable applications require being prematurely optimized to the extreme because they absolutely need to shave off that cycle from an allocation.
Meanwhile people allocate memory in preparation of a HTTP request, or just before the application is stuck in idle waiting for the user to click on the button that closes the dialog box.
It makes absolutely zero sense to proselytize about premature optimization when no real world measurements are on the table.
People mostly stopped using C++ to develop web servers which handle web requests, because they moved to Java, C#, PHP, Ruby, Python, etc. People mostly stopped using C++ to develop GUI apps which handle these buttons, because they moved to Java, C#, and now JavaScript/TypeScript.
What’s left for C++ is software (or sometimes individual DLLs consumed from other languages) which actually need it to achieve the required performance. Even despite the language is unsafe, low-level, and relatively hard to use which directly affects software development costs.
However, for Java or modern C#, in my experience the performance is often fairly close. When using either of them, very often one doesn’t need C++ to be good enough.
Here’s an example, a video player library for Raspberry Pi4: https://github.com/Const-me/Vrmac/tree/master/VrmacVideo As written on that page, just a few things are in C++ (GLES integration, audio decoders, and couple SIMD utility functions), the majority of things are in C#. Still, compared to VLC player running on the same hardware, the code uses same CPU time, and less memory.
Aren't you contradicting yourself? You started off complaining malloc of not being good enough, but now it's suddenly ok to tolerate Java and C#'s performance drop when compared to C++?
Which one is it?
This is a discussion on C++.
> People mostly stopped using C++ to develop web servers which handle web requests, because they moved to Java, C#, PHP, Ruby, Python, etc.
I'm not sure you understood what I said, or thought things through.
By the way, the top performing web framework in the Tech Empower benchmark is a C++ framework which uses C++'s standard smart pointers.
https://github.com/drogonframework/drogon
Also, one of the most popular web frameworks for Python started off as an April Fools joke. I'm not sure what's your point.
Lastly, the main reason why C++ ceased to be the most popular choice in some domains was because it was during a very long time the most popular choice in some domains, and still remains one of the most popular choices. Some of the reasons why C++ dropped in popularity is the fact that some vendors decided to roll their own alternatives while removing support for C++. Take for instance Microsoft, which was once responsible for making C++ the only tool in town for professional software development. Since it started pushing C# for all sorts of web applications, multi-platform applications, and even desktop applications, and also pushing the adoption of those technologies as a basic requirement to distribute apps in its app store, developers can only use technologies that exist. But does that say anything about the merits of C++?
Over time, it became less important for C++ to be a good general-purpose language. When performance of idiomatic C++ is good enough, using C++ is often a bad idea: it delivers comparable performance to C# or Java, but it’s more expensive to use. While technically C++ has desktop GUI frameworks, web frameworks and others, they aren’t hugely popular: due to development costs, people typically prefer higher level memory safe languages for that stuff.
For use cases like videogames, HPC and similar, C++ has very little competition, because that level of performance is borderline impossible to achieve in other languages. It’s for these use cases people care about costs of malloc, cache-friendly RAM access patterns, and other things which are less than ideal in idiomatic C++.
On the Apple and Google side that ship has long sailed, with C++ used only on the lower OS levels, and as basis for MSL.
Naturally there are still the GUIs for game consoles left (although XBox dashboard uses React Native, with the previous generation being UWP, PS 4 famously used WebGL), and special purpose embedded devices.
All of this code built just fine on pre c++11 compilers, ran reliably, was performant and easy to maintain.
Rolling your own smart pointers is not something I'd discourage.
If we're still on the topic of game engines, someone who cannot easily figure out a smart pointer implementation has no place working with the game engine's code in the first place.
Unreal Engine has its own implementation for that stuff and it took me literally minutes to get to grips with it - same with the custom engines in companies i worked at before. This has absolutely never been a real problem in practice.
There are lately some improvements to that, like making move/forward an "intrinsic" from the point of view of the debugger. Also the SIMD interactions seemed to be a problem in certain loop vectorization.
As for "you are clever bc you do not use move constructors"... I can be clever and use assembly instead of C. But I am not sure it would be the right choice in all scenarios. If you have a tool and can take advantage of it, just make use of it.
An enormous amount of "smart" old school c++ is at best equivalent to a well written modern equivalent, and at worst more dangerous, error prone and slower. Anything that uses references for writable out parameters combined with a bool return value (or no return value) for success is a great example. On my last project, I rewrote a bunch of fundamental Unreal Engine path handling methods, replacing the old school bool GetXXX(TCHAR* In, TCHAR* Out); with FStringView GetXXX(const FString& In);
In the process of doing so I found multiple engine and game bugs that in the best case were logic bugs and in the worst case were memory safety issues. I also left the original function call in place and made it wrap the new version with no perf overhead for the callsites I didn't change, and a 30% increase for the cases that I did fix. Almost all of that is because I stopped requiring null checks, I was able to lean on RVO and move semantics, and I was able to avoid unnecessary initialisations in many places in the code. Most of the sites I replaced were calling FString ThingStr; TCHAR* Thing =<...> if (GetXXX(FooStr.c_str(), Thing)) ThingStr = FString(Thing);
And most were replaced with FStringView Thing = GetXXX(Foo);
> write very simple C++98 code, maybe a bit of C++11 but that's it. They keep it simple.
Assuming of course there's already a smart pointer library that you're using, modern c++ allows for simpler, more correct and faster code in many many places. Some examples are auto:
for( map<string, int>::iterator it = map.begin(); it != map.end(); ++it)
becomes
for (auto& it: map)nullptr is now a thing over NULL, move semantics make it feasible to write logical code without needing to jump between logic and type manipulation for basic operations, static assertions exist, enum class, attributes (nodiscard, noreturn, deprecated),constexpr and all the evolutions that came with it, structured bindings, concepts, if/for initializes...
All of the above are just off the top of my head features that I interact with daily, even if I don't write them daily, all of which make my code and project safer faster and easier to understand.
> This new C++ syntax is just a gimmick that we don't need.
I disagree. It has many useful properties. It gave us the spaceship operator which massively simplifies the code needed to write correct objects. A single well written spaceship operator can mean numerous algorithms just work on containers of your type, rather than custom filter/search operations. The syntax of left to right definitions for functions _seems- superfluous but it drastically reduces the amount of work needed to be done by the compiler which should translate into real world compilation speedups. The import/include swaps could allow for new code to interact with well behaved old code in a safer manner. As someone else said on this thread, it's not always about one feature in isolation, it's about combining multiple features and the result being worthwhile.
That's something all JVM languages share. Not that easy with compiled languages unfortunately.
Worse, because C++ libraries are gigantic header files, you have to support 100% of the language to use them.
/* Proposed "new" c++ syntax */
main: () -> int = {
cout << "hello world\n";
}
// vs.
/* Classix c++ syntax */
int main() {
cout << "hello world\n";
}
The "new" way looks a lot like a copy of the newer Java syntax for closures. x := 5
Where you can optionally insert the type between the colon and equals, otherwise it is deduced x : int = 5 x : t = e
Was called "headform" and was the only syntactic form for the whole language. (Expressions used a shunting yard algorithm to desugar into a sequence of headform.)The language included modules, classes, parameterics, functions, constraints, ..., whatever.
ducks
ALGOL, the := in programming languages used for assignment comes from IAL which became ALGOL 58. It's definitely not a modern development. Go reintroduced it to popular mainstream languages, sort of. Except they still use = for assignment and := is used when it's also declaring a new variable.
It was done in the very distant past. It was done in the intervening time. And it's done now. "Modern" trend?
Besides, it makes it trivial for text editors to implement "go to definition", and many other niceties without a real semantic parser.
ML uses `name: type = value`. It came out over a decade before C++ and just one year after C.
[0] https://cstheory.stackexchange.com/questions/43971/why-colon...
90s fashion is also trendy right now. Which is great because a few years ago it was 80. The cycle continues!
For another example, Python goes all the way back to 1991, with v1.0 in 1994. But it didn't really start to trend until early 00s.
Sure, if we redefine "recent" or "current" we can twist words to reach whatever conclusion. In reality, this ": type" syntax is neither new nor recent, nor is it "a current trend". Kotlin is also not recent. A decade is a lot.
You conveniently left Scala out, I suppose because it didn't fit your argument.
> "For another example, Python goes all the way back to 1991"
Well, since we are discussing Python, the syntax for its type hints looks like what exactly? And it was discussed as far back as 2014. Is that "recent"?
Nevertheless, it is definitely the case that the "name: type" syntax is noticeably more trendy now than it was, say, 15 years ago. Sure, there were plenty of languages that had it already back then, including Scala (and ML, and Delphi, and ...). But how many of them were mainstream? Whereas now, this syntax is embraced by Python and TypeScript.
And not even OP claimed that the syntax itself is new or recent; I mean, everybody who knows about Pascal knows how old it really is! They literally said "it's an old idea" that is "currently trendy".
Unless you now have named loops in modern C++ now this removing goto is problematic.
auto main() -> int {
cout << "hello world\n";
}
The difference between the two is mostly the : and the =. The colon is for declarations being written: i : int = 0;
Instead of int i = 0;
main is the variable/function above and its type is ()->int and it’s being assigned, i.e. defined, hence the =. This syntax removes a ton of parsing complexity and makes the language more regular. Functions are defined and assigned similarly to variables.if (cond) { } requires 4 presses of Shift (or 2, if you combine them).
if cond: foo requires 1 press of Shift.
I'm not sure what part of my comment makes you believe that I think the latter comes out worse than the former. If anything, the fewer keypresses probably strongly helped Python's success without people necessarily realizing it. Moreover, this might very well be one thing hindering more widespread adoption of Python's type annotations.
You also need to consider how common each construct is. A class declaration requiring 20 excessive keystrokes is not necessarily worse than a variable declaration requiring 1 excessive keystroke.
Also consider this: lots of hugely frequent characters and symbols that don't require SHIFT in EN-US keyboards do in other keyboards layouts, for example Spanish. So this cannot be a consideration, at least not a major one.
It's kind of weird to see you assert that with no attempt to establish its truth when Scala is something like... the ~20th most popular language? Whereas Python, C, C++, Java, VB, C#, etc. are right at the top. So either this is one of the (obviously many) factors hindering wider adoption of Scala, in which case the relative popularity is entirely consistent with this, or it isn't a factor, in which case the it would make sense to present at least some evidence to the contrary given that your assertion goes against the popularity rankings.
> Also consider this: lots of hugely frequent characters and symbols that don't require SHIFT in EN-US keyboards do in other keyboards layouts, for example Spanish. So this cannot be a consideration, at least not a major one.
I'm pretty sure the popularity of {most software technologies} outside the US and inside the US are very far from being independent variables.
The barrier to adoption of Scala has to do its functional programming side, as well as its complex libraries and types.
Kotlin also uses ": type" syntax, by the way. As does Python!
> I'm pretty sure the popularity of {most software technologies} outside the US and inside the US are very far from being independent variables.
It's hard to understand your point. Are you arguing that programming languages are less popular outside the US? Or "most software technologies"?
So let's sum it up: SHIFT concerns are very minor in comparison to other programming language ergonomics (in which need I remind you, C++ never fared very well), and the ": type" syntax is very common and mainstream in programming languages, with no hindrance to adoption.
Most of the time you spend thinking about what you should be typing.
So optimizing for the number of keystrokes is optimizing for the wrong metrics
Does `{` not require shift on your keyboard?
You misread my comment. I'm talking about cases where existing prevalent syntax for that construct does NOT already require holding Shift. Python is better in this respect since ":" requires fewer presses of Shift than "{" or "}". This is not the case when you go from "int x = 1;" to "x: int = 1;".
Moreover, the inconvenience of going from 1 Shift to 2 is not the same as the burden of going from 0 to 1. Especially because you can often combine the 2 into 1 without releasing the modifier key in between.
And furthermore, variable declarations are far more common than class or function declarations, so you can't weight them equally.
Most programmers I know use native+US layouts (like you), a minority always uses US (like me), almost nobody uses a native layout to code.
What I would do is this:
- Replace them with a single quote -> E' instead of È or É and so on.
- Ignore all uncommon marks.
In most contexts, it is perfectly acceptable to do that. A native speaker would understand without giving it a second thought or considering it impolite. I write emails to my boss like that.
If I had to make something meant for the public (say a UI, or a document), then I'd switch to the Italian layout for that purpose only and then go back to the US one.
Been using Scala for 7 years, and it has never even occurred to me this was a "burden".
Think of it like font kerning. Some people notice it, some don't, but most people prefer good kerning even if they never think about it.
On another note, anything with "->" is just painful to write, same thing with the (in his proposal) ":_" instead of "=" for function arguments. Just annoying.
template<typename T>
void hello(T msg) {
std::cout << "Hello " << msg << "\n";
}
hello: (msg: _) = {
std::cout << "Hello " << msg << "\n";
}
I kind of like this, actually, because so many people I've worked with are afraid of templates in C++, but they seem to have no trouble with essentially equivalent code using generics (in other statically typed languages) or dynamically typed languages where it "just works" (until it doesn't). Not that we'll be adopting this any time soon, but I suspect they'd be more amenable to this form when `template` seems to make them quake in their boots. void hello(auto msg){
...
}
Is legal today (since C++20 IIRC) and it is equivalent to your first example.As for -> being painful to write - that's obviously subjective, but more importantly, given that it's the operator you use to access members via pointer-like things (including smart pointers and iterators), it's already pervasive in C++.
int i = 0; // pretty
i : int = 0; // ugly
i will stick to c++03 thank u very much.
syntax "beauty" is just a matter of how used to it you are (excl brainfuck ofcourse). Admitting this, is quite a big present one can give to him/her self.
i: int 0 = ;
int x = 0 "There is an integer x with value 0"
x: int = 0 "x is an integer with value 0"
x: int 0 = "x is an integer that is zero valued"
The last one is awkward, the first one is harder to read.
Something like "i: int" is superior (at least in Scala), because the type annotation is optional. You can drop it, and just say "val i = 42". I believe it simplifies the parser as well.
There is a reason many modern languages are adopting the style.
int i; // i is a local int
int i(); // i is a global function returning an int
int i(0); // i is a local int initialized to 0.
Yes, there's uniform initialization now. But this whole thing was a problem in the first place because of poorly designed declarator syntax that doesn't extend well and is difficult to parse unambiguously.It can actually get worse, to the point where it's impossible to tell whether a given line of code is a declaration or not. E.g. "a<b>c" can be a declaration "a<b> c", or it can be an expression "((a < b) > c)", depending on what "a" is, exactly. This can be taken to 11 with templates:
template<size_t N = sizeof(void*)> struct a {
template<int> struct b {};
};
template<> struct a<sizeof(int)> {
enum { b };
};
enum { c, d };
a::b<c>d;
The meaning of this code now depends on whether sizeof(int)==sizeof(void*) for a given implementation on which it runs. Now, granted, this isn't the kind of thing you'd find in real world code outside of IOCCC and the likes; but it's legal, so the tooling has to be able to process it anyway. And by "tooling" I mean anything that has to parse C++ at some point - not just the compilers, but also debuggers, IDEs etc.Would you people object to ':' for assign and single '=' for equality ?
int i:42
if i=42 print "ok"Practically speaking I find : to be challenging to quickly scan out of source visually. In golang this objection is muted slightly because struct initialization usually ends up being clearly indented by the formatter.
Y U NO fn??!? So close, and yet so obviously wrong.
I still haven't read enough about syntax 2 to understand if it allows any other use of ( after a : but if yes, a keyword like fn or fun would make the parser much more regular.
Why not take this opportunity to streamline the calling syntax? Streams are no different than regular function calls, e.g.
cout.output("hello").output("world\n")
Even so there is now std::format
The two examples on Wikipedia are both caused by this, even though it's not explicitly spelled out:
int i(int(my_dbl)); // two calls to int's constructor
// in Java/C# style syntax:
int i = new int(new int(my_dbl)); //no ambiguity
TimeKeeper time_keeper(Timer());
// in Java/C# style syntax:
TimeKeeper time_keeper = new TimeKeeper(new Timer());
C++ could have also chosen to reserve a keyword for initialization even for in-place values, just like they did with `new`, but they chose to re-use function call syntax instead.I would also say that just moving the type after the variable name doesn't fully solve the issue - it's the : syntax for initialization that does this. Here is a hypothetical C++ where the syntax remained the same as today, but (return) type was moved at the end of a declaration:
typedef double my_dbl;
typedef int i;
foo (my_dbl double ) void {
i int (int(my_dbl));
//unambigous: i is an int initialized with an int constructed from my_dbl
f (double) int; //unambigous: f is a function from double to int
g int (my_dbl); //unambigous: g is an int constructed from my_dbl
bar((my_dbl)i);
//ambigous: calling bar with i cast to double, or calling it with a function from my_dbl to i (double to int)?
} X *y;
Are you declaring a pointer, or are you performing multiplication? It depends on what “X” is.Similar story with C strings vs Pascal strings, another can of worms.
variable: Type [= initial_value];
// initializes foo to an int with an initial value of 43
foo: int = 43;
// initializes main to a (presumably, as I haven't read the
// spec yet, const)pointer to a function that takes no
// arguments and returns an int.
main: () -> int = {
...
}Sorry for kind of x-posting (I posted in r/cpp the questions below), since I did not get any replies, but, Herb Sutter or anyone close enough, if you are around and have some time for questions... would love to hear more from this project.
I just saw the whole talk. Most ptomising C++ replacement so far for me: it is immediately useable and compatible and it does NOT duplicate standard lib and it is 100% compatible.
That said, here is my feedback/questions:
About pass by copy. Dave Abrahams in his value semantics talk says copy is too eager in C++. Swift for example uses something sinilar to COW, avoiding eager copies when passing parameters. This also makes all copies de-facto noexcept. Is this the idea with Cpp2?
About bounds checking: is it possible to call a non-bounds check function and detect it?
About bounds checking: why always checked access? There are contexts, for example in a range for loop, where checks csn be ellided safely. Also, if you do checking in a span subspan, that slice traversal should not need checks.
About bounds checking also: why the 3 subscript is a run-time error? I think it could be made a compile-time error for constants and constant folding.
About overloading: one thing not mentioned in the talk. how are overload sets handled when you introduce the same name in the same context if parameter passing now is a bit different?
About classes (no design yet, I know): there will be memeber functions vs free? How would overload resolution work? In case there are both memeber and non-member funcs, associated namespaces and so on. Also, it is interesting to look at Swift protocols and Val lang views here. Polymorphism comes from use, not from type itself.
about exceptions: is it compatible to have lightweight exceptions and exceptions? my concerns here are two: should I annotate with try the calls and the result type of the function is affected (returning some ki d of outcome, reault, etc.). This is really viral compared to throwing from deep stacks and less usable as a default IMHO.
Sorry for the typos. Typing from a phone.Herb is great. Glad he's thinking about this. C++ syntax is garbage and needs help, but why deviate so much from existing syntax? Why drop curly braces? The decision to allow mixing new/old C++ syntax in the same file seems like a gross mistake that prevents a more incremental fix of the language. Why the heck doesn't it support defining classes yet? Is it because of the aforementioned requirement to intermix new/old syntax?
I hope efforts like this continue, as we all know something is broken in C++ syntax. I think it can be fixed with tweaks and some breaking of backwards compatibility in the same source module.
What do you mean? It uses curly braces for compound statements. But it doesn't require curly braces to define a one-line function.
It's also something that was adopted by other languages in the curly braces family - e.g. C# originally required curly braces for function bodies, but now allows you to write:
int Sum(int x, int y) => x + y; std::mt19937 e; // Mersenne Twister engine
std::uniform_int_distribution<int> u(0,99999); // uniform ints 0 to 99999, inclusive
auto rng = std::bind(u,e); // so rng() retuns the next random number
And filling a vector with random numbers could look like this: std::vector<int> nums(1000);
std::generate(nums.begin(), nums.end(), rng);
This last line can be written in C++20 more simply like this: std::ranges::generate(nums,rng); for (int i = 0; i < nums.size(); i++) {
nums[i] = rng();
}
Simple, easy to understand, actually compiles to efficient code even in Debug mode, don't need to include <algorithm> header which includes 10000+ lines of STL code which bloats compile times, etc. for (int i : nums) {
nums[i] = rng();
}
Edit: Ooops, I'm an idiot, this doesn't do anything useful (i is every value in nums, not the indices of nums...)Like, wouldn't it be
for (int num : nums) {
num = rng();
}
(I don't know any c++). I kind of expect the above not to work though because it's doing an assignment. I suppose this is a good argument to just use a `fill` or `generate` function... for (int i = 0; i < nums.size(); ++i) {
nums[nums[i]] = rng();
}
Which happens not to go out of bounds simply because nums[i] is initially 0 for every i in the range - so it sets nums[0] to a different random number 1000 times.Usage examples have often been updated to current best practice.
* The C++ Programming Language, 4th edition by Bjarne Stroustrup - The biggest change in the language was the introduction of C++11 and this book covers it well.
* A Tour of C++, 3rd edition by Bjarne Stroustrup - Summary of all changes upto C++20.
* Software Architecture with C++: Design modern systems using effective architecture concepts, design patterns, and techniques with C++20 by Adrian Ostrowski et.al. - More like a broad survey rather than in-depth but covers development in a "modern ecosystem".
* Modern C++ Programming Cookbook: Master C++ core language and standard library features, with over 100 recipes, updated to C++20, 2nd Edition by Marius Bancila - A series of articles one each for a language feature.
Now, please do it. Context-free syntax is so obviously better that it's really an embarrassment, people are still defending hacks from decades ago.
And please, for the love of Knuth, someone do it with LaTeX, too.
My guess is either yes, macros, or if you're going to write a new language, you can skip C++, output object files, and make them compatible at link time.
Scroll down to "A Modest Proposal: C++ Resyntaxed".
Yes please - I really like this approach. C++ as a bare bones on-its-own language is not going to find adoption in the new world - it is way too much baggage & legacy, syntax and functional complications and adding more complexity every 3 years is just counter productive. If this can become something like what Scala or Kotlin are to Java it would be good for C++.
At that point, why not just upgrade to C++20? Carbon, in particular, is left behind in the C++17 world, so it's outdated by C++20 (not to mention future developments).
In particular Google hasn't even started to adopt C++ coroutines, threading, ranges, or modules (they have their own module implementation which is actually better in most ways, but different)
It remains my all time favourite language to do crazy stuff in, but I try to avoid crazy stuff these days!
https://github.com/SerenityOS/jakt
Really nice feature set.
'p.x' just like 'p.x' in C, but
'p^' instead of '*p' and
'p^.x' instead of 'p->x' and
'p^^.x' instead of '(*p)->x'.
Just admit it: Pascal is nice, clean, compositional, and less surprising.Don't you remember? Or do you honestly think that C syntax is better?*
I scrolled the whole Readme. Are we supposed to parse the parser in /source ?
https://github.com/hsutter/cppfront/tree/main/regression-tests/test-resultsPersonally I think he should switch to using namespace std; as default as well. The compiler can warn if you alias the names, and all the std:: in C++ code is just noise and annoying to read and write.
As for std::get, while it's a separate issue I think the problem that solves should be solved in a more elegant way if you're trying to reform the language anyway. This is not elegant:
std::get<0>(arr) = 1;For the past few days all I can see in HN is the announcement of new languages and research projects that relate to C++:
* Carbon (https://github.com/carbon-language/carbon-lang)
* Val (https://www.val-lang.dev/)
* Jakt (https://github.com/serenityos/jakt)
* now Cppfront...
Is everything alright within ANSI / ISO committee?A breakaway group, including Google and Microsoft, has sought to break backward link-compatibilty, and has failed. So they are pursuing other avenues.
Probably the single most valuable improvement possible while retaining C++'s strengths would be to fix initialization syntax and semantics. Second would be to simplify and limit implicit conversion and promotion for base types.
[1]:https://users.monash.edu/~damian/papers/HTML/ModestProposal....
CppFront:
> My specific goal is to explore the question: Can we make C++ 10x safer, simpler, and more toolable if C++ had an alternative "syntax #2," within which we could be completely free to improve semantics by applying 30 years' worth of C++ language experience without any backward source compatibility constraints?
SPECS:
> We propose an alternative text-based syntactic binding (called SPECS) for the existing semantics of the C++ language.
So while SPECS is semantically equivalent to C++ as it is today, CppFront isn't bound by that constraint.
Sutter makes this very clear later in the README:
> Important disclaimer: This isn't about 'just a pretty syntax,' it's about fixing semantics.
Just to be clear, my intent isn't to diminish Conway's work in any way. Both are interesting projects, just with different goals.
This can be actually seen as cppfront having less chance than SPECS.
Simply incredible from my mortal programer point of view.
Wow!
Mark
And, just to also quibble on the syntax: using `:` for identifier types, lambdas, ranges, and scope resolution is deeply unfortunate.
i’m assuming Cppfront is breaking backward compatibility with old c++ codebase.
I guess the only thing cppfront would offer is mixed syntax within one file, but I could even see Carbon implementing something like that.
And their development in the last 15 years is just trying to escape the oo cage they were put in.
Something that fixed C++ would have been a godsend in 2004. Nowadays... just a nice thing to have.
Gang of Four patterns, Turbo Vision, OWL, MFC, Motif++, PowerPlant, AppToolbox, VCL, Qt, wxWidgets, Telligent, CORBA, DCOM,....
First let's look at "toolable":
My big issue with Modern C++ these days is not just that it's complex to write, but that it also takes a long time to compile, what with all the template/compiler magic going on in the background. Long compile times break concentration, which I don't like. C++2 is not going to improve compilation times, because it's going to be compiled to C++, and then undergo another compilation step. If I combine C++'s slow compile times, two compilers, and all the dumb bugs that I can create and have to debug+fix, I get ....... (pls wait compiling) ..........
Let's switch context and look at "simpler":
Yay functions look more like JS lambdas or whatever. Fine. Let's look for the pointer-y stuff. Like [1] -- in C or "normal" aka "pre-modern" "C++1" you'd have:
int x = 42;
int *p;
p = &x;
func(*p);
In the "simpler" "C++2" you have: x: int = 42;
p: *int;
p = x&;
func(p*);
I sure hope the changed syntax helps with parsing complexity or whatever, because it's not any different for me. Seems like C++2 got jealous of Rust and wanted to remind everyone "hello fellow kids, C++ is also cool pls". Note that even Rust and Go follow the C-style "*p" pointer deref syntax instead of "p*". What happens if I combine "C++1" and "C++2" syntax in the same file? I'm not as smart as Herb Sutter -- Is "p**x" a pointer deref then a mul? Which variable is being dereferenced? Perhaps it is a double deref, or ...? So much for simpler. (also note I haven't talked about template magicks here)What do we have left ... "safer"? sighs
Okay, C is unsafe. Yes, C++ has to carry that baggage. Yes, memory corruption is horrible. Yes, buffer overflow errors lost lots of money, and have caused lots of wailing and gnashing of teeth. Yes, Modern C++ alleviates some of that unsafety with templates and a smart compiler. Yes, Rust has a smarter compiler with better static analysis that helps write safer code. But all this statically analyzed safe code still has to provide C wrappers if it's a shared object, or use the C wrappers provided by whichever kernel it's running on. So somewhere underneath all this safety lurk the familiar monsters, and someone's going to reach for them in the name of performance (remember Actix?). What about all the other issues that you can have unrelated to buffer overflows (log4j)? Will C++2 protect me from my own foolishness? Perhaps that's why its so complicated -- all my C++2 code is safe, because I will write no C++2 code.
And another thing: why not just do the Raku or Kotlin thing and call it by a different name, and have it be a separate language compiling to C++? Why "C++2"? Calling it C++2 vs C++ makes me think of Python 3 vs Python 2, and oh man that is not a good connection to hint at when claiming full backwards compatibility. Python 3 also got quite complicated compared to Python 2, so that doesn't give me hope either.
Now that I think about it, shouldn't this be called C+=2?
TL;DR:
- C++2 is not going to be more toolable because ultimately you're still compiling template-heavy C++
- C++2 is not going to be more simpler because unusual pointer syntax, confusion when mixing C++1, template magicks still have to exist
- C++2 is not safer because you can/have to still write or interface with C/C++ and the footguns therein.
- C++2 should not be called C++2, pls let's find a Raku/Kotlin.
Let C++ be the complexity monster it is. That's the tradeoff we make when we have to write it. If safety is the issue, can't we just use a sandbox or something? Is C++2 really the better option?
[1]: https://github.com/hsutter/cppfront/blob/main/regression-tes...
C++ is (I'm guessing) currently holding up an order of magnitude more applications than whatever you think is better than it. Clearly it has upsides, so it's baffling to me when people greet attempts to reduce the downsides with either "This doesn't make sense, just scrap it for something else" or endless nitpicks about the approach chosen.
A smart guy has decided to put his time towards improving a hugely influential language. That seems like an uncontroversial positive to me, and I welcome any useful results.
Of course it's difficult to equate that kind of advice/feedback to negative comments on HN. But often, there's some kernel of truth there. So while there's some merit to C++, the criticism can equally be valid. And keeping the underlying complexity of C++ (similar to how Carbon will) might not meaningfully simplify development).
Do you know such people who work on large software systems, as opposed to, say, micro-controller firmware, or kernel drivers and such?
(Asking as a person who maintains an important(ish) C library for embedded coders: https://github.com/eyalroz/printf)
I do myself fall in the category of people sticking to C over C++. I used to love C++ back until c++03, then completely hated the whole "new generation c++" orientation that started with c++11 and pretty much decided to not touch c++ anymore.
Redis is written entirely in C.
Still, if could quote Redis developers making the GP's claim, that would count.
I liked C++ in some ways, but as a whole, I think, C++ didn't reduce -- and may have increased -- the complexity of our software compared to the complexity of an equivalent C implementation. (I'm talking about the complexity of the software itself, not the complexities of the tasks it was doing.) The distribution of the complexity in the code would just have been different between the two implementations. IMHO.
So I mostly stick with C or other non-C++ languages now. Of course, C++ has expanded and changed greatly since then.
But I have also heard that although the senior devs would prefer to stick to C for technical reasons, they have started to use/embrace C++ for hiring reasons (since many grads are taught C++). So I wouldn't be surprised if finding "pure" C codebases/ones without C++ is getting harder. The eco-system around C++ (e.g. test frameworks) certainly seemed healthier. It's been a few years though, so I might not have a good picture any more.
So what. No one's going to rewrite all of those million lines of code in New C++, or whatever they call this incompatible syntax. It's just a distraction from more relevant efforts.
"I'm sharing this work because I hope to start a conversation about what could be possible within C++’s own evolution to rejuvenate C++, now that we have C++20 and soon C++23 to build upon."
Clearly this is relevant for c++ itself (coming from Sutter), so I'd say it's quite unfair/misguided to call it "just a distraction from more relevant efforts."
I could easily see a company like Meta adopting this. They have both a huge amount of C++ code as well as actively developed guidelines and internal libraries that make use of cutting-edge features.
What's getting harder and harder to see now is why, if you need to write new or rewrite now, you'd choose C++ over rust. In the long run that's a recipe for only the most gnarly old codebases being written in c++ and no one wanting to touch them.
It's all C and C++, and Python if we need a scripting front-end. (Prototypes are done in matlab). "Rust" doesn't even exist in our universe. I don't think the people on HN are really in touch with how real people program in the real world.
And on few the occasions when we have to do a complex desktop GUI app, we'll use C# or F#. We can get cross-platform Windows/Linux easily this way.
I mean, I’m a real programmer in the real world. I think most people on here are. Rust is already in heavy use at Microsoft, Amazon, and Google, it’s not some fringe thing anymore.
Hell it's going into the kernel in 6.1.
C++ runs a real risk of surviving only in the embedded/realtime space in the next 10 years.
C++ is all the operating systems and the browsers and the games and the JVM.
C++ has been hated for decades now. The reason it's used is often because you have to.
> C++ runs a real risk of surviving only in the embedded/realtime space in the next 10 years.
Ten years from now C++ will still be the language underpinning LLVM, web browsers, geometric modeling, machine learning, etc
Embedded/realtime? No way. The people I know in embedded won't touch C++ with your 10 foot pole.
Most of my embedded code is wrangling various "stacks" into cooperation with one another, and C++ helps me not one whit with that. At the other side, when I'm just poking sensors, C is more than enough.
And, as much I would really like embedded communication stacks to be in Rust, all that would happen would be the vendors slapping "unsafe" on everything so they would basically be writing C anyway.
Yes, everyone knows that, except for HN users! While I'm sure there are managers at large companies who let some employees play with Rust, it's not used.
The thing that obscures this, I think, is that at most of them the surface area that the intersection of C, C++, and Rust that is high availability, security critical software, makes up a relatively small portion of what they do no matter what language it's in.
So while there's a lot of C++ at say, Google and Facebook (but relatively little at Apple IME), very little of it needs to be in c++ let alone Rust.
But where it matters? You better believe big companies are shifting towards "if you're starting new you should seriously consider Rust" (if not a mandate). And once you let one other language into your mix, the question becomes: why's all the high level stuff written in c++? May as well start new projects in Go.
Some are farther along than others but it's a thing.
It may be changing, but certainly not faster than I’ve observed (it’s not a matter of speculation, for me).
I enjoy this[1] annual series of blog posts on Apple's usage of Swift in iOS. I just did a quick and dirty, but similar analysis of Apple's usage of C++ of the macOS 12.5.1 install on my computer. I extracted my dyld_shared_cache, and then used find and nm to count up binaries containing unstripped C++ symbols. This undercounts the usage of C++, since sometimes it's used only internally in stripped binaries, and I also think the number of binaries metric probably undercounts the importance of C++ because the ones that do use C++ tend to be more significant binaries, but even still it gives some idea of the scope of C++'s use at Apple.
About 25% (559 / 2292) of the libraries in the dyld_shared_cache contain unstripped C++ symbols. About 15% (22 / 154) of executables in /System/Applications (so 1st party apps / helpers that ship with the system) do.
That said, I think you're probably right that things are changing. Probably there are lots of people at Apple thinking of how they can replace C++ with Swift in their code. But on the other hand, I would not be surprised if we can still find significant uses of C++ in whatever macOS release we have 10 years from now.
[1] https://blog.timac.org/2021/1219-state-of-swift-and-swiftui-...
I spent 8 years at Apple, mostly working in C and then rust, both even smaller pieces of the Apple language pie.
But I also did a little c++. I have commits in swift for eg (though I wasn't on the team and they were pre-oss, so you'd have to dig real deep to find them now).
Most of the services side of the company, which is where I was, was java ime. A lot of shift to Go in the last few years though. And that's a very quickly growing part of the company where you can't do a local check to find out language use. It's also the only part where rust is viable, because the product side of the company has much stricter limitations on what they'll build for distribution and as of when I'd left, adding rust to that mix was not even on the radar there.
But the places where rust was gaining were small but important, which is the main thrust of what I was saying.
But anyways, with how siloed apple is experiences can differ a lot, even beyond normal for bigcorps. :/
https://halfblood.pro/the-story-about-net-cross-platform-ui-...
I like to use Xaml. For iOS/Android there are solutions, too.
No. C is the lingua franca for exported public stable ABIs, which is an extremely small subset of any given program's ABI usages.
C++ ABI is just as widely used as C's, just for internal unstable linkage instead of stable exported linkage. So yes you still need to rewrite in bulk to move off of C++, unless your code happened to be tiny & only used C API libraries.
I'm sure many would dislike rust if they were forced to use it. Same with python, etc, etc, etc.
I'm not saying the criticisms are wrong per se, but the reason we have so many languages is probably because there's no 'right' language.
New dialects and features were getting added to JS all the time, but what happens is people writing JS libraries or tooling would watch how far these features spread in their users' browser compile base, and many of these various features would never even make it into popular JS runtimes, while others are everywhere now.
I think it's a reasonable model for development--lots of people trying to improve things, the community slowly sifts it out, and the standards are the most conservative of all.
Patching over the old work--hopefully without breaking anything--always is. I'm just stating that much of the hostility toward C++ comes from the fact that we have so many superior options to choose from now--options which profited from the lessons C++ learned the hard way, and incorporated them into v1 instead of patching them in as options at v23.
And this is not to strikeforce anyone with a "Rewrite it in Rust!", but to suggest that C++ is maybe not the best choice for new work in 2022.
Not a single compiler supports the standard library as a module. No compiler has full support for modules.
Modules are a C++20 feature that isn't usable in real projects in 2022. And there's no signs that modules will be ready anytime soon.
I suppose when GCC and MSVC will both support modules in an interoperable way, these same people will enjoy and praise this development.
People are complaining because it’s 2022 and support for modules is seemingly not there or incomplete in all these places, and modules are talked about in some C++ communities as if they’re a thing that is actually usable (for example Bjarne’s talk at Cppcon a few days ago).
So they're complaining on the internet about not getting free stuff fast enough.
They could work with the maintainers of their toolchains instead. If everyone that used xcode complained to apple, C++ modules would be done there already. MS likewise would probably put more resources on it. Open source is a little more complicated, but Red Hat and Canonical do have paid products.
This is a very weird take.
Visual Studio isn't free for professional use. Xcode is free, but publishing for macOS/iOS is not. Both tools exists to serve platforms owned by the first and third largest companies in the world by market cap. Microsoft and Apple don't spend tens of millions of dollars a year on employee salaries for these tools out of the goodness of their heart.
In any case, the issue isn't with the toolchains. The issue is the C++ committee created a specification that has turned out to be problematic. Your next response may be to join the committee and fix it from the inside! Google tried that and rage quit to create Carbon. Sutter hasn't quit, but I think Carbon and Cppfront getting announced the same summer is not an accident.
Very Online engineers complain about slow progress on free stuff for the open source toolchains. And they don't bother to light up ticketing systems on paid products. Microsoft and Apple don't spend as much on these things as you would expext. Because relevant management thinks we don't really care that much. But the common thread is that sitting back and expecting the world to come to you isn't reasonable.
Incidentally, Google is a big place. Some Googlers are still involved in the ISO committees. Certain Googlers, admittedly influential ones, lost patience and started betting their reputations that certain dramatic moves would be a better choice.
I personally don't think ISO is the presidency of C++. C++ culture focuses on language design way too much and engineering (good third party libraries, supporting implementation, empirical evidence, etc.) not nearly enough.
This is the perennial "If you only used modern C++, you'd be fine."
Sorry. People have been repeating this for 15 years, and it hasn't gotten any more true.
For example, C++ still doesn't have a useful string type--everybody rolls their own. How do you interoperate when 2 different C++ libraries can't even agree on something as basic as what a "string" is? The existence of "header-only" libraries and the contortions they go through is a tacit admission that compiling and linking C++ is still a disaster. C++ still doesn't have a stable, documented ABI--so everybody defaults to C (thus throwing away any theoretical usefulness C++ might have). Embedded shuts down 90+% of C++ beyond "C with classes" because they can't use exceptions and can't allocate memory dynamically. The preprocessor is a brain parasite that has grown with C/C++ and can't be removed anymore without killing the host. etc.
In fact, I would argue the lack of stable ABI is the only thing propping C++ up. Because the C++ ABI is so shitty, you have to make the top-level code C++ which then calls into the other libraries and subsystems which actually have useful, stable and documented ABIs and interfaces.
If C++ had an ABI that didn't suck, you could invert that, drive the C++ from the better language, and everybody would relegate C++ to increasingly tinier libraries until they could excise the remaining code and throw it into the trash.
I feel for the people who put in their entire lifetime trying to "improve" C++. However, it's time to admit that C++ can't be fixed and move on.
How amazing would it be to have people like Stroustrup and Sutter being paid to work on a language that doesn't start with unfixable suckage?
People will still use it, and that is entirely sensible and rational, and it makes sense to improve it. Long-term, maybe C++ will fade into the same kind of sunset that Fortran and Cobol currently occupy.
I’m not gonna make fun of Fortran users, or tell them to move on. There’s even a Fortran 2018.
Trying to allocate people to only the “best” possible work—say, replacing C++—is just poor allocation of resources.
Example, back in the heyday of C++ frameworks bundled with compilers, bounds checking strings and collection arrays were the default.
Nowadays you either call at(), have your own bounded checked types, or enable the debug library, but naturally without influence on 3rd party depedencies.
All that you've indicated is that it's used a lot, which is obviously because it has decades on every other option.
Anyway, I love Herb Sutter and I love this work he's doing. Awesome stuff.
It’s holding up lots of applications because better languages didn’t exist at the time those projects were started, and it’s rarely feasible to switch languages. Specifically, a lot of people look to Rust to unseat C++ for new applications, but it will take a while for Rust to mature with respect to libraries (e.g., game engines) and mindshare in those industries. But even then, old languages have tons of staying power by virtue of age.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
looks quite decent, but overall Rust is still behind? So if I am in an area where performance (or cost/energy) is king then I'd still pick C++.
>Can you write assembly embedded in Rust code?
Yes, I've done this several times. It looks like this: https://doc.rust-lang.org/rust-by-example/unsafe/asm.html
Yes. https://docs.rs/simd/latest/simd/
> Can you write assembly embedded in Rust code?
Also yes. https://doc.rust-lang.org/reference/inline-assembly.html
Inline assembly: https://rust-lang.github.io/rfcs/2873-inline-asm.html
Performance is almost identical, even the linked benchmark is quite even.
Yes, Rust has inline assembly in roughly the same way you'd be used to with C or C++
https://doc.rust-lang.org/reference/inline-assembly.html
The Benchmarks Game has a bunch of benchmarks, and while it's probably significant that the Python programs are routinely orders of magnitude slower than say C, we likely shouldn't read too much into whether somebody scraped a few more milliseconds off the best time for one of the benchmarks listed.
Yes... unlike in x64 MSVC, one of several C++ compilers I frequently must support, where inline assembly is verbotten. Does that put Rust ahead? :P
There are plenty of good reasons to hate on C++, but that’s conflated with a lot of “real hacker” signaling.
Most takes on C++ lament that it isn’t C, or is too complex. Rust is a quality replacement for C++, but it doesn’t succeed on either of those counts either.
All new code I write is in Rust or Haskell. If I need to do something weird, like with DynamoRIO, C++ it is.
I think "the internet" tends to complain about languages which have accrued complexity over time, and contain parts that are now considered bad but still lurk in the shadows tripping you up.
Scala, C++, perl and others seem to fall into this category.
There's a very real desire for things to just be laid out for you, and much less regard for whether the workflow/tradeoffs associated with that beget quality software in the process.
It is hard to not see this as an army of advanced beginners unconsciously trying to avoid doing the work required to level up.
I don't think it's hard at all. "I don't want to have to manually manage dependencies" isn't "avoiding leveling up", it's just not doing pointless work that computers are good at doing. Similarly, there's negative value in requiring every/most project to script its own bespoke build system (CMake) when 99% of projects can fit a mold (and then there are efficiencies when 99% of those projects fit that mold--e.g., trivial cross compilation). None of this stuff is meaningfully related to "leveling up". Similarly, bombarding people with language primitives that are almost always footguns (e.g., inheritance) isn't really helping anyone "level up" except to know not to use those features.
In the C++ world, you have an army of people who think they're experts because they've navigated all of these problems to find something that sort of works, but in practice they're far less productive and often don't know what they're missing out on from the rest of the industry. I would rather have people who are productive but don't pretend they're experts.
Lack of modularity and the fact that everything is treated like a single source file (via #include) adds interesting ways of unexpected interactions.
You could say the same thing about COBOL.
(Not that I think that C++ is as bad as COBOL.)
Some probably only saw it during their format education, and it's most likely been taught like C with classes and/or they had to use for stupid things.
There are also many forms of C++, there is your "old and regular" C++, game dev C++, there is modern C++, then there is "I only know C, but sure I can write C++" C++.
Pulling dependencies is not as simple as `npm i boost`.
Communities are small, segmented and not welcoming to newbies.
Absolute madness with build tools. I've never worked with CMake myself until last year, and I haven't so frustrated.
As for how many applications using C++ that's not really an argument — C++ was literally the only choice for many of them at that time.
My experience was negative overall and traumatic to some extent and I wouldn't rule out that some who were first exposed to the language in college or even HS share the same aversion and feel scarred and haunted forever by that Blue Ghost.
If that doesn't show the power of a language, I don't know what can. I don't really think the community is toxic or unwelcoming, more like people are too used to ctrl+c ctrl+v their way out of problems. Nothing against that, I'm the same way, but it really is a matter of taking your time with the language. I've honestly learned to applaud most rust fanatics because of this, at least they are passionate about learning athe language inside/out.
I think it's pretty easy to forget the foothold these languages have, and their flexibility. We all have seen the perseverance of COBOL.
I hope Rust does well.
Being old isn't a superpower. That said, I don't see C++ replaced in certain areas for loooong time.
Of course not many people ever got properly introduced to C++.
I welcome good replacements for C++'s syntax, because it's genuinely terrible.
People have been trying to make the language safer and more palatable for multiple decades now, so it seems reasonable to allow Sutter to have a go at it.
There are people who are totally unproductive at C++ and find it scary, mainly because they are not very exposed to it. They assume everybody is as uncomfortable and unproductive with it as they are. Nay, it is impossible for anyone to be productive with it, simply because they aren't. They will attack evidence that somebody has done well with it, because it is some defense of ego for them.
There are other complaints about C++ beyond this of course, with validity, and many of them from old timers and people who bitterly complained about it for decades from a place of knowledge and experience. People have been complaining about ugliness of C++ for longer than my own career. However, as more and more people come up in the post-C++-as-fashionable era, I think this above theory is more and more the bulk of the complaints.
People have been working to reduce the downsides of C++ for decades at this point, and the results are incredibly lackluster, and frequently only work in trivial toy examples - if even then. Every now and then I try out the latest and greatest I can find in terms of dangling pointer prevention, and find myself confused as to if I've even enabled the proper checks, when even my trivial toy examples fail to trigger said checks, only to be horrified when I in fact have enabled said checks and gained nothing.
I, too, welcome any useful results. The problem is: I don't actually find the results useful. I get told garbage like "C++ is safe now!" by people who apparently don't know any better, for things underperforming existing static analysis tools from over a decade ago. It's a distraction, a false promise, and a waste of my time - which could be better spent reviewing C++ code, improving existing static analysis coverage, or going full ham and unironically pushing for incremental rewrites in Rust. Not because it is perfect: I've written and debugged my share of dangling pointer bugs in unsafe Rust code too - but because it is at least better, and provides me with tools to fight the good fight.
And if, from time to time, you find my voice amongst the nitpickers of attempts at C++ "improvements", it's perhaps because the attempt has failed, and I would rather not have others waste their time like I have on false promises. Granted, such warnings are unlikely to work: the alure of something that might ward off yet more late nights chasing yet more heisenbugs in yet another C++ codebase caused by coworkers, third parties, those long gone, or perhaps worse - by me - is just too promising. But it might.
Show me real bugs, in real codebases, caught in bulk where existing tools have failed, and you will have my undivided attention.
IMHO nothing but Rust or Go has any merit as a replacement unless it also bring the same safety. A fully unsafe language in 2022 is dumb. (Fully unsafe, not optional unsafety which may be needed in a systems language.)
I think this is a subtweet at Zig, and while I am not a zig developer, I will point something out that everyone forgets (to defend C++ and Zig and the like): Some apps don't need memory safety.
For example a single player game or application that runs just on your own machine, memory safety would just be another type of bug.
If dealing with Rust's ownership rules is annoying and you don't want a GC, then you can certainly use an unsafe language. Not all apps are websites and browsers.
I would also add (I could be wrong here) but I think things written in web assembly can also be unsafe, as the memory is in a sandbox. I think I heard this once, but I could be wrong.
Even apps that run locally and don’t talk to the net can be vulnerable. How many break ins happen via bugs in PDF viewers, office packages, and media players? Look it up. PDF and spreadsheet vulnerabilities are very common.
Then there’s the productivity gain you get by not having to spend hours debugging obscure memory and threading bugs. The latter are not ruled out in Go or Rust but they are profoundly less likely to occur, making code that actually takes advantage of modern hardware much easier to write.
After learning Rust and after having used C++ very heavily for years I can say that Rust is far more productive.
True. The issue is that they operate on complex input that can arrive from anywhere. So if you download a malicious PDF file it can exploit you.
But GP's other example is valid: A singleplayer game running locally will only run the game assets from the developer. The user input (in almost all games) is very limited. Exploits are not a major issue in such a program.
There is room for languages like Zig. There is also the potential for it to be used in improper places, but so far the majority of applications I've heard of seem reasonable to me.
> Then there’s the productivity gain you get by not having to spend hours debugging obscure memory and threading bugs.
Also true. But there are the other usual tradeoffs, such as that a language focused on simplicity and correctness, like Zig, might reduce other sources of bugs.
Some developers might be more productive with Rust, others with Go, others with Zig. I don't think there's a single answer here. As an example of another related tradeoff, fast compile times often help debugging, which is an advantage of Go and Zig.
Nothing is that isolated anymore. Can you find a modern single-player game on Steam that doesn't talk to the network for analytics or DLC or sending crash reports to the developer? I'm sure they exist, but they're likely exceptionally rare.
For Zig to be helpful in the modern world, it will need memory safety guarantees and it will need them on by default. It's totally OK if those are easier to switch off than in Rust so that you can do the low-level things performantly and easily. But you have to start from a default position of safety.
I disagree every language needs to start with the same defaults as Rust. Rust proved its approach is a useful one, and Rust is a huge asset to our industry, but we also benefit from exploring other approaches.
Even setting security considerations aside, as soon as you have users, using a non-memory-safe language means having users reporting very unhelpful “bun crashes at startup with "segmentation fault"”and good luck to figure out what's going on.
> I think this is a subtweet at Zig
Why do you think it's especially aimed at Zig? Zig is only one among others, like Carbon, or Cppfront discussed here.
> Why do you think it's especially aimed at Zig?
I honestly don't use zig so I really don't have a horse in the race, but when I read: "nothing but Rust or Go has any merit as a replacement unless it also bring the same safety. A fully unsafe language in 2022 is dumb"
That seems to be targeted at replacing C++, which both Carbon and CppFront are not doing per se. Zig is the next big "low level replacement language" (that isn't Rust or Go) that HN seems to chat about so I took a guess.
Again, I don't think that's too relevant to my point (although I am curious if OP wants to weigh in on what they meant).
If you genuinely believe that Rusts rules slow developers down by “several ordrers of magnitude” (which litterally means: “makes you 100 times slower or more”) then you've probably listened/read way to much anti-rust discourse for your own good…
Rust has a learning curve and in the beginning you're obviously going to be slowed down by the learning process, but for 99% of the tasks[1] once you've gone past the initial learning period, the rules aren't slowing you down since you've internalized them, and compiler errors end up being mostly about things that would have caused a memory bug in other low-level languages.
[1]: that is, everything but custom graph data structures, which are really hard to get right without a GC anyway.
If I had to make a game right now though, I would be way faster in C++.
That said though, I do hope as I learn rust everything becomes obvious. I would be very happy if I get equally as productive (since that means I can use rust for everything).
I guess I just worry that certain things are like custom graph data structures, where suddenly ownership is a huge issue.
I've no doubt about that, learning Rust sometimes feels like having to swim with your hands tied in the back.
> That said though, I do hope as I learn rust everything becomes obvious. I would be very happy if I get equally as productive (since that means I can use rust for everything).
Good luck:), don't be afraid to stop and try again a few weeks later if you get too frustrated: I struggled a bit to learn rust early 2016 and after a few weeks of night hacking, I had no more free time to carry on and I was still very confused about all of that. Then, I stopped for almost 6 months, having read of few blog posts in Reddit in the meantime but nothing especially eye opening or anything, and when I got back to Rust for some reason everything sounded clear and I completed my first project in Rust very smoothly, as if my brain had been slowly digesting the concepts in a background thread during that time.
> I guess I just worry that certain things are like custom graph data structures, where suddenly ownership is a huge issue.
Advice: avoid them as much as possible (most of my former use of graph-like structures where in fact related to programming patterns and not strictly necessary for what I wanted to do) and if you're really manipulating graphs, you should probably use a dedicated library (I think petgraph is the most popular but these kind of things can be workload dependent so you might need to go for another one).
Maybe not, but it's still a big pain in the butt to avoid bugs in that area. I imagine that static analysis for memory safety would be something that most developers would use in existing unsafe languages if they could.
> Many years later we asked our customers whether they wished us to provide an option to switch off these checks in the interests of efficiency on production runs. Unanimously, they urged us not to--they already knew how frequently subscript errors occur on production runs where failure to detect them could be disastrous. I note with fear and horror that even in 1980, language designers and users have not learned this lesson. In any respectable branch of engineering, failure to observe such elementary precautions would have long been against the law.
-- C.A.R Hoare on his Turing award speech in 1981.
If a play Civ V and it crashes, I just a bit mad.
I will acknowledge though good engineering practices are important, maybe my perspective of "tolerance" of these bugs when they are isolated is missing the point.
Thankfully App stores are already making this point clear to the industry.
Second, I think data shows most people don’t return games after 1 crash. In fact, I think getting to market fast is a bigger factor for profit.
People still don't do it because they have been taught that software is special, they have to put up with broken products, that is just how things are with computers.
Removing type checking from your running programs is like wearing a life jacket for your practice emergency drills and then taking it off as soon as your ship was really sinking
https://www.youtube.com/watch?v=YYkOWzrO3xg
Maybe that was right in the 70-80s, but I feel like he's not giving enough credit to modern static verification / type-checking.A bug that can be costly to reproduce and fix and has the affect of terrible UX and losing unsaved progress. If I had to choose a category of bugs to tolerate memory safety bugs would be near the bottom of the list.
I know that in the domains that I'm interested in, I would be way miserable programming in something like e.g. Java, because I've figured out certain patterns that I know will work in a memory-unsafe language with very little friction, but that cannot be simply be translated to Java because of e.g. reference semantics.
The same probably holds for other approaches to safety, like Rust. I'm not interested anymore in tight opinionated approaches to problem solving. I _love_ void-pointers and memcpy'ing stuff because these let me do abstraction with very low friction. Getting the job done very quickly, with very very low rate of bugs, and those get quickly fixed. (I'm not making a statement about multi-million line projects in corporate environments. This is about the situations that I run into, personally).
Lack of education + Legacy software / programmers / education material.
Honestly? And this might be a hot take - exactly the same way as I'd explain people rejecting structured programming way back in the day. It was the new thing on the block and people were not used to it, and preferred to just sprinkle GOTOs in their program just like they always used to do rather than learning a new paradigm.
As someone who used to professionally program in C++ on an expert level in the past and now has completely switched to Rust I can tell you that (at least subjectively for me) Rust is way more productive than C++ ever was. Once you internalize how the language works the limitations of Rust just disappear. You learn how to instinctively write code that's idiomatic, and it just stops slowing you down. But it also gives you a ton of new features that end up speeding you up compared to C++, e.g. sum types, pattern matching, cargo, great standard library, destructive moves, proper modules, #[derive], good error messages, etc. Memory safety can also be significant productivity boost because you can (or at least I do) just churn out code without thinking about it too much and just rely on the compiler to make sure that it's memory safe. Can this code trigger use-after-free? Is this iterator valid? What's the lifetime of this pointer? Instead of thinking about all of these you can use those brain cells for something else.
Plus it's only single player until they decide to add multiplayer…
Many of us don't agree with the way the committee has been "evolving" the language and then mandating "the one true way to do Modern C++". At least with this "syntax 2" thing they will leave the language alone.
Perl had Ruby, Python, and PHP attacking it. C++ has Rust, Go, Nim, and Swift (albeit that only Rust is a direct challenge, the others are taking market share for things that used to be C++). I've even seen people using Vala in new places.
Perl was hard to use. The new languages addressed many of the ergonomics, semantics, and design concerns. Rust addresses a lot of C++'s dangerous baggage.
Perl had novel tooling at the time, but eventually wound up behind. Newer languages brought more to the table. Rust has Cargo.
Perl tried evolving for a long time. Eventually birthed a new language altogether (Raku), albeit that the old language still receives updates. (Python also saw this.) Google recently unveiled Carbon. Now there's Cppfront. The C++ language committee is doing different things...
It's a question of which ecosystem evolves faster and ultimately which ecosystem attracts more fresh blood as old timers age out. AFAICT, younger developers are flocking to Rust instead of C++. There are a bunch of Rustlang streams on Twitch.
As someone who did a lot of Perl in web 1.0 times, and haven't really been a full time programmer in a long time but occasionally have since written Ruby, Python, and PHP, but never really felt those languages were that huge of improvements over Perl (certainly now their ecosystems are, but I'd love a much better entrant to successfully "attack" Python), and would sometimes wonder what if Perl had just managed to evolve significantly instead...makes me appreciate and even root for efforts to "make C++ 10x simpler, safer, and more toolable" like this one or perhaps Carbon, even though Rust does seem like a huge improvement.
I think the C++ analogy makes sense. How many programmers entering the field today are choosing to learn C++ when they have so many other choices without all of C++'s baroque complexity and footguns?
The language complexity was famous and eventually grew into several subsets before UNIX adoption wiping out most systems languages that sprung as PL/I alternatives.
However I see one similarity between C++ and Perl: for decades, they evolved a lot from there ecosystems and nowadays, both C++ and Perl could be difficult to learn just because you can't figure out what module to use to get the job done. Also Perl suffers the lisp curse http://winestockwebdesign.com/Essays/Lisp_Curse.html (I don't know about C++).
You really don't. The vast majority of projects will never change the language version they use. Simple projects are simple to fix. Remaining large projects have enough resources and expertise to do changes across their entire code base - Chromium has recently changed more than 15000 instances (in over 8000 files) of using raw pointers to using raw_ptr instead in a single pull request [0]. How? They wrote a Clang based tool which did it automatically [1].
[0] https://chromium-review.googlesource.com/c/chromium/src/+/33...
[1] https://source.chromium.org/chromium/chromium/src/+/main:too...
A more common example, what if a library that you use stops supporting the old version, and the latest compatible release of the library has a security vulnerability.
I'm talking about using the same language version (standard), not compiler version - e.g. the latest Clang still supports the C++98 ISO version.
> A more common example, what if a library that you use stops supporting the old version, and the latest compatible release of the library has a security vulnerability.
You backport the fix or the change library. Nobody said it's easy but your project should not hold the entire language hostage.
Now, many languages, including C++, have gotten away with smaller backwards-incompatible changes (e.g. C++ 17 removed dynamic exception specifications from the language entirely - getting away with it because it was an almost entirely unused non-feature).
Edit: removed some reference to PHP6 where I had a wrong recollection.
They all have their places, but if your code is littered with them, it feels like every line is a puzzle.
Here's an attempt to wrap sqlite in a typesafe manner using template expansion. All very simple C++11. https://github.com/nurettin/pwned/blob/master/sqlsafe/sqlsaf...
(some types had to be spelled out because the compilers weren't ready)
Here's some more C++11 without using any of the mentioned complexities https://github.com/nurettin/pwned/blob/master/server/server....
This shows that simply spelling out your types and not getting crazy with polymorphic metaprogramming makes code much more readable. Ideally, code should just be a bunch of ifs and loops.