EDIT: found one on wayback: https://web.archive.org/web/20241124225457/https://herecomes...
package management belongs to the os - or at least something else.
don't get me wrong, package management is a real problem and needs to be solved. I'm arguing against a language package manager we need a language agnostic package manager.
If we are talking about global shared dependencies, sure it may belong in the OS.
If we are talking about directly shared code, it may as well belong in the language layer.
If we are talking about combining independent opaque libraries, then it might belong in a different "pseudo os" level like NPM.
Point 1: I do not want my program to only run on only one OS, or to require custom code to make it multi-platform.
Point 2: What if there's no OS?
To run on only one OS at build time? I usually just set up cross-compilers from linux if I am making cross-platform C/C++ code.
>Point 2: What if there's no OS?
You can use a system like bitbake I think.
Python doesn't have a language package manager, you're free to use pip or poetry or uv or whatever, but it does have PEP 517/518, which allow all Python package managers to interact with a common package ecosystem which encompasses polyglot codebases.
C++ is only starting to address this problem with efforts like CPS. We have a plethora of packaging formats, Debian, pkg-config, conan, CMake configs, but they cannot speak fluently to one another so the package ecosystem is fractured, presenting an immense obstacle to any integration effort.
This is a long-standing pain point. LWN has a series of reports covering this, one of which is: https://lwn.net/Articles/920832/
Comparing it to other languages isn’t really fair since they don’t have polyglot code bases in the same way, and where native packages exist in for e.g. Npm, then you run into the same problems anyway.
Languages need to be more agnostic than a package manager requires because I should not have to rope another organization into my trust model.
Cargo already goes too far in encouraging a single repository (crates.io) for everything through its default behavior. Who maintains crates.io? Where is the transparency? This is the most important information the user should know when deciding to use crates.io, which is whether or not they can trust the maintainers not to backdoor code, and it is rarely discussed or even mentioned!
The default cargo crate (template?) encourages people to use permissive licensing for their code. So that is an example where you are already making implicit political decisions on behalf of the ecosystem and developers. That is alarming and should not be for the language maintainers to decide at all.
In C/C++ you have a separation of the standard from the implementation. This is really what makes C/C++ code long-lived, because you do not have to worry about the standard being hijacked by a single group. You have a standard and multiple competing implementations, like the WWW. I cannot encourage the use of Rust while there is only a single widely-accepted implementation.
Additionally, you do not have to necessarily enforce these things on the language level, the standard and the tooling could live as two independent projects coming from the same entity. You could still use the compiler and the libraries from your OS, and build the code like that, or you could just reach out to an optional standardized tool that serves as a glue for all the external tools in a standardized way.
Yes, there are a lot of valid concerns with this approach as well, but personally for me, as a frustrated C++ developer, who is most likely going to still use the language for a decade to come, I feel like all the other languages I had mentioned in my previous post had addressed what is my biggest point of frustration with C++, so it's definitely an issue that could be solved. Many tried to do it independently, but due to personal differences, no funding, and different ideas of what should be the scope of such tooling, we ended up with a very fragmented ecosystem of tools, none of which have yet to date been able to fully address an issue that other languages solved.
You and I must be using two very different versions of Cargo, because on mine the default template doesn't specify a license.
You're working with some seriously hairy technologies, dealing with very knotty compatibility issues, and you don't want to learn... Docker?
I find this odd because it's relatively simple (certainly much simpler that a lot of what you're currently dealing with), well documented, has a very small, simple syntax and would probably solve your problems with much less effort than setting up a third development machine.
> any open source project should consider docker not an option to solve their problem
That's generalising far too much.
That and a better package manager so your clang wrong version problem cannot have. Which is what I was trying to get at.
It clearly doesn't except if you're a fan of dll hell and outdated packages.
The solution to…a problem created directly by a specific approach is to…do even more work ourselves to try and untangle ourselves? And just cross our fingers and just _hope_ that every app/library is fully amenable to being patched this way?
Alternatively, we could realise that this isn’t really feasible at the scale that the ecosystem operates at now, and that instead of taking an approach that requires us to “do extra work to untangle ourselves” we should try and…not have that problem in the first place.
>And just cross our fingers and just _hope_ that every app/library is fully amenable to being patched this way?
It requires some foresight in designing the application, and whether or not you even choose to use that application in the first place. We should strive to decrease the complexity of the system as a whole. The fact that packages are using different versions of the same library in the first place is a canary and the system should disincentivize that use case to some extent. Using static libraries or a chroot or a sandbox for everything is sweeping the problems under the carpet.
>taking an approach that requires us to “do extra work to untangle ourselves” we should try and…not have that problem in the first place.
I would prefer a system that allows you to link every application to the same library as a default, but also allows for some per-application override, perhaps by using symlinks. That would cover the majority of use cases. But I do not think that dynamic linking is generally in vain.
In my own projects, I try to rely on static linking as much as possible, so I understand your perspective as a developer. But as a user I do not want programs to have their own dependencies separate from the rest of the system.
Applications ship their lock files + version constraints. Gets merged into a user/os level set of packages. You update one package, OS can figure out what it has to rebuild and goes off and does that.
Still shit-out-of-luck for anything proprietary, and it’s still super possible for users to end up looking at compile failures, but technically fits the bill?
I really think it is. Even at the scale of a single app it may sometimes make sense to have multiple versions of a same library, if for instance it implements a given algorithm in two different ways and both ways have useful properties
If there were guarantees that every library would always be both forwards and backwards compatible, that would be reasonable. Sadly, that's not the case.
The solution is to be more professional. DLL hell comes from libraries that break compatibility: serious libraries should not break compatibility, or at least not often. Then when they do and you happen to have the issue, it's totally fair to go patch the library you depend on that depends on the breaking lib. Even in proprietary software.
The modern way is to use ZeroVer [1] and language package managers that pull hundreds of dependencies in the blink of an eye. Then asking that people compile everything themselves or use the one system deemed worthy of support (usually Windows and the very latest Ubuntu). And of course not caring about security one bit.
[1]: https://0ver.org/
Basically, once you have an OS level package manager, you have issues of versioning and ABI. You have people writing to the lowest common denominator - see for example being limited to the compiler and libraries available on an old Red Hat version. This need to maintain ABI compatibility has been one of the hugest issues with evolving C++.
The OS package manager ends up being a Procrustean bed forcing everything into its mold whether or not it actually fits.
Also, this doesn't even have the issue of multiple operating systems and even distros which have different package managers.
Rust and Go having their own package managers has helped greatly with real world usage and evolution.
By dumping all the same file types in massive top-level directories, you need a separate program (the package manager) to keep track of which files belong to which packages and dealing with their versions and ABI and stuff. Each package represents code developed by a specific group with a certain model of the system's interoperability.
GoboLinux has an interesting play on the problem by changing the directory structure so that the filesystem does most of the heavy lifting.
Let’s say we make a “thing” which contains packages for all participating languages.
98% of the time, aren’t users just going to go “filter down to my language” and just continue what they’re doing, except with a somewhat worse overall experience, depending on whatever the “lowest common denominator” API + semantics we use for this shared package management solution.
Multi-language build systems already exist, which happily serve the needs to those projects which find themselves needing cross-language (+distributed) builds. Could there be some easier versions of these? Sure, but I don’t feel like “throw everyone in the same big box” is the solution here.
It has to be - while nobody needs more than a subset of that big box, the intersection of what everyone needs turns out to be throw everyone in the same big box. If you have anything less than that one big box you end up many standards and then everyone chooses which standard and in turn something important you need choose the other standard and you can't use it (ie the situation we are in now)
Of course making that "one standard to rule them all" easy enough to use is a hard problem. It may be itself impossible and thus everyone drops back to the current mess.
> the intersection of what everyone needs turns out to be throw everyone in the same big box
I don’t follow: what’s the intersection of JS/Python/Go/Rust package management? What are they all needing that isn’t “download and store packages”? It can’t be OS level configuration, because that’s going to vary by OS and language.
Will the GhostBSD maintainers pin the right version of Haskell's aeson package?
Will the Fedora Asahi devs stay on top of the latest Ocaml TLS developments?
Will MS package PureScript's code for DOM manipulation?
Os package managers do a fundamentally different task than dependency management tools used in development.
They ship a bunch of applications and the libraries you need to run the applications.
If you need different version of libfoo than e.g. Firefox does, you're out of luck.
Need to support a customer with an older release which needs a different version of libfoo? Not gonna happen.
Unless you're talking about Nix or Guix, your OS package manager is not a substitute for a dependency management tools.
If such a thing came to C++, there would obviously be limitations around module boundaries, when different modules used a different Edition. But perhaps this could be a way forward that could allow both camps to have their cake and eat it too.
Imagine a world where the main difference between Python 2 and 3 was the frontend syntax parser, and each module could specifically which syntax ("Edition") it used...
C++ is a mess in that it has too much historic baggage while trying to adapt to a fiercely changing landscape. Like the article says, it has to make drastic changes to keep up, but such changes will probably kill 80% of its target audiences. I think putting C++ in maintenance mode and keep it as a "legacy" language is the way to go. It is time to either switch to Rust, or pick one of its successor languages and put effort into it.
I agree but also understand this is absolutely wishful thinking. There is so much inertia and natural resistance to change that C++ will be around for the next century barring nuclear armageddon.
After all doubtless COBOL's proponents did not regard COBOL-85 as the last COBOL - from their point of view COBOL-2002 was just a somewhat delayed further revision of the language that people had previously overlooked, surely now things were back on track. But in practice yeah, by the time of COBOL-2002 that's a dead language.
From my point of view C++26 is going to be the last one that actually matters, because too many are looking forward to whatever reflection support it can provide, otherwise that would be C++23.
There is also the whole issue that past C++17, all compilers seem like a swiss cheese in language support for the two following language revisions.
Rust has the concept of _crate_, which is very close to the concept of compilation unit in C++. You build a crate by invoking `rustc` with a particular set of arguments, just as you build a compilation unit by invoking `g++` or `clang++` with a particular set of arguments.
One of these arguments defines the edition, for Rust, just like it could for C++.
But! We'll be able to see all the extra parsing happen so in theory you could track down the incompatibilities and do something about them.
That is not possible. The the following function in C++ std::vector<something> doSomething(std::string); Simple enough, memory safe (at least the interface, who knows what happens inside), performant, but how do you call that function from anything else? If you want to use anything else with C++ it needs to speak C++ and the means vector and string needs to interoperate.
I'm working on interoperation with existing C++. It is a hard problem and so far every answer I've found means all of our new features still needs to be written in C++ but now I'm putting in a framework where that code could be used by non-C++. I hope in 5 years that framework is in place by enough that early adopters can write something other than C++ - only time will tell though.
There is nothing memory safe whatsoever about std::vector<something> and std::string. Sure, they give you access to their allocated length, so they're better than something[] and char* (which often also know the size of their allocations, but refuse to tell you).
The point of an iterator is to make it hard to do that. You can, but it is easy to not do that.
> modifying the vector while iterating it
Annoying, but in practice I've not found it hard to avoid.
> adding to the vector from two different threads or reading from one thread while another is modifying it
Rust doesn't help here - they stop you from doing this, but if threads are your answer rust will just say no (or force you into unsafe). Threads are hard, generally it is best to avoid this in the first place, but in the places where you need to modify data from threads Rust won't help.
This is just not accurate, you can use atomic data types, Mutex<> or RwLock<> to ensure thread-safe access. (Or write your own concurrent data structures, and mark them safe for access from a different thread.) C++ has equivalent solutions but doesn't check that you're doing the right thing.
Nitpick: editions are specified per crate, not per module.
---
Also note that editions allow to make mostly syntactic changes (add/remove syntax or change the meaning of existing ones), however it is greatly limited in what can be changed in the standard library because ultimately that is a crate dependency shared by all other crates.
Is that an ABI thing? I thought all versions up to and including C++23 were ABI compatible.
Also, Rust compiles the whole world at once, so any ABI breakage from mixing code from different compiler versions doesn't happen. (Editions are different thing from compiler versions, a single version of the compiler supports multiple editions.)
For normal libraries, an and b could depend on different versions, so this could be a problem. The name mangling scheme allows for a “disambiguator” to differentiate the two, I believe that the version is used here but the documentation for it does not say if there’s more than that.
libc++ and glibc++ both break ABI less frequently than new versions of C++ come out. As far as I'm aware, libc++ has never released a breaking change to its ABI.
Now there is the possibility that someone could come up with a new breaking syntax and want a C++26 marker. However nobody really wants that. In part because C++98 code rebuilt as C++11 often saw a significant runtime improvement. Even today C code built as C++23 probably runs faster than when compiled as C (the exceptions are rare - generally either the code doesn't compile as C++, or it compiles but runs wrong)
I only ever worked in a couple of codebases where we had one standard for everything that was compiled and I suppose that's what 90% of people do, or link static libs, or shared libs, so externalize at an earlier step.
So purely a thought experiment.
Implicit capture of this in lambdas by copy.
std::iterator removed.
std::uncaught_exception() removed.
throw () exception specification removed.
std::strstream, std::istrstream, and std::ostrstream removed.
std::random_shuffle removed.
std::mem_fun and std::mem_fun_ref, std::bind1st and std::bind2nd removed.
There are numerous other things as well, but this is just off the top of my head.
Note too that we are reasonably good (some of us are experts) at C++ and not Rust. Like any other human when we first do Rust we will make a mess because we are trying to do things like C++ - I expect to see too much unsafe just to shut up Rust about things that work in C++ instead of figuring out how to do it in safe rust. (there will also be places where unsafe is really needed) I want to start slow with Rust so that as we learn what works we don't have too much awful code.
C++ has its own Core Guidelines that are pretty rusty already (to be fair, in more ways than one). There's just no automated compiler enforcement of them.
I knew they are bad, but I don't think it should be removed.
Javascript/Node/Typescript has even more identifiable factions.
I think developing factions around these things is unfortunately normal as languages grow up and get used in different ways. Rust has arguably tried to stay away from this, but the flip side is a higher learning curve because it just doesn't let certain factions exist. Go is probably the best attempt to prevent factions and gain wide adoption, but even then the generics crowd forced the language to adopt them.
C++ does a lot, but has a big disengaged crowd, for many reasons, and that crowd will suffer from the push forward. Python and Node are similar.
Source: I am in both factions, as are my colleagues :)
You're already using a language with a strong type system, so it's confusing to me why you would choose to draw the line here.
Yes because then I don't have to spend hours writing esoteric spaghetti code to prove something to the compiler that is trivially known to be true. Your error is assuming static lifetime checking is free. As an engineer, I use judgement to make context-dependent trade offs.
If you like playing the compiler olympics, or your employer forces you to, please use Rust.
Well said.
This is why i am firmly in the Stroustrup camp of backward compatibility/zero overhead/better-C/etc. goodness of "old C++". I need to extend/maintain/rewrite tons of them and that needs to be as painless as possible. The current standards trajectory needs to be maintained.
The OP article is a rather poor one with no insights but mere hoopla over nothing.
And that’s exactly the reason why we need more safety in C++.
I’m terrified at amount of code in real world written with this mindset.
I actually love the ideas that Rust brought forth. It definitely has a place in the ecosystem, and I'm glad to hear critical software is being rewritten in Rust! But that doesn't mean that C++ should copy it.
Is it fine if it silently gives the wrong answer? If so, why are you bothering with the software at all?
In my experience all nontrivial C++ codebases have silent memory corruption bugs (at least when built with popular compilers).
- Webkit, GCC, and a few others are non-trivial C++ codebases that are (I argue) useful.
- In your experience, since they are non-trivial, they have silent memory corruption bugs (i.e. they are not "perfectly safe").
Does this answer the "why bother with software at all" question?
Your examples of GCC and Webkit are both projects that have spent enormous amounts of effort to be as memory safe as they can be, and have both had many memory safety related CVEs in the past. As was already pointed out, you still have to pay the cost of engineering memory safe code, even when your compiler/static analysis doesn't have your back.
Not at our org. Though I know a couple of die hard fans that will eat you for lunch if you do something stupid or ugly.
The GCC/Webkit examples were not the best examples, but were nevertheless easily available examples that made one particular point: OP's comment was self-contradictory.
And I'd say that even with all that additional effort, it has a level of bugs that's not "fine". Indeed, per the article, I suspect that the maintainers of Webkit are some of the people pushing to make C++ more Rust-like.
Fundamentally I don't think this is a case where C++ makes a deliberate design tradeoff that makes sense for some projects. I think it's just a bad design choice (not even a choice as such - it wasn't a question that was considered at all when C++ was first designed) that should be corrected. Sometimes there is a right answer.
Indeed. And when that "right answer" comes along, it tends to swipe away everything else. If it's universally better, why wouldn't it?
Except that, Rust does not do that. Which is a hint that it's not an "universally right answer", but a right answer for a subdomain of problems. That's basically what I was trying to say. That it does come with its own tradeoffs/downsides.
(maybe I'm wrong and it's only a matter of time until that happens; but I don't think so.. it's been a while, there was time for it to make the impact. Lifetime annotations are not yet adopted by any other mainstream language, AFAIK)
> Except that, Rust does not do that. Which is a hint that it's not an "universally right answer", but a right answer for a subdomain of problems. That's basically what I was trying to say. That it does come with its own tradeoffs/downsides.
Rust may not be the only right answer, but memory unsafety is the wrong one. New projects overwhelmingly pick memory-safe languages, governments and organisations are banning memory-unsafe languages at least for new projects. I don't think anyone is picking C++ at this point if they don't already have a big sunk cost invested in it (even if that cost is just their personal programming experience).
> Lifetime annotations are not yet adopted by any other mainstream language, AFAIK
Linear Haskell is getting there, but most languages aren't flexible enough to retrofit lifetimes (or at best it would be a multi-year effort, like adding types to Python) - as we're seeing in this whole C++ discussion. Also non-GC languages are niche in the first place, and the problem lifetimes solve is a lot less urgent in a GC language. I don't think any post-Rust language has hit "mainstream" yet (we only really get a couple of new mainstream languages a decade), so we'll see what happens in the future.
If you think we should instead evolve C++ so that safety isn't mandatory I'm right there with you, but it's not where the language is today and that discussion has also been shut down by the evolution working group. Moreover, Bjarne's policies mean that telling the critical software people to go fuck off to a different language fundamentally isn't part of the plan either.
That is already a desirable place to be, where you managed to get a working implementation ready to evolve. My issue with opinionated languages like Rust is that they make development more expensive. I then afford to pay the necessary work-effort for fewer projects than I otherwise could if I was to focus more on the problem(s) at hand instead of that and other mandatory constraints forced upon me by the compiler. I very much want my development tools to limit themselves on being tools, to assist me on the part of the problem I chose to focus on with little to no cost paid for their usage. I want to be able to focus on prototyping some working solution first, and only then, if the project's needs really warrant it, to switch on paying the development cost for other aspects, be it safety or whatnot.
I don't think you've ever done any serious work with lifetimes. I've been a rust developer for a number of years, and I have never once encountered a situation where the rust compiler forces me to add annotations for something which is trivially true. Never.
What actually happens is 95% of the time I never have to add lifetime annotations anyway because the compiler infers the correct annotation from the lifetime elision rules. The remaining 1 in 20 instances is when the borrow checker yells at me, and literally every single time it is due to a latent logic bug in my code. For example, accessing memory after it's been freed, or using a container after it has been consumed. Stuff that C++ would call "undefined behavior" and are generally considered Very Bad Things by C++ developers as well.
It boggles my mind that you don't want the compiler to tell you that “you have a logic error here.”
I'll have you know I made a variable void* just yesterday, to make my compiler shut up about the incorrect type :D
The problem is that the rules enforced by Rust is not restricted to lifetime rules, it's a much much larger superset that includes quite a lot of safe, legitimate and valid code.
C++ isn't beholden to Rust's trade-offs either. There's a whole spectrum of possibilities that don't require broken backwards compatibility. Hence: "Why draw the line specifically at lifetime annotations?"
Rust shines in the other 95% of code. I spend some time every morning cleaning up the sorts of issues Rust prevents that my coworkers have managed to commit despite tooling safeguards. I try for 3 a day, the list is growing, and I don't have to dig deep to find them. My coworkers aren't stupid people, they're intelligent people making simple mistakes because they aren't computers. It won't matter how often I tell them "you made X mistake on Y line, which violates Z rule" because the issue is not their knowledge, it's the inherent inability of humans to follow onerous technical rules without mistakes.
It seems very fair to tell them to just use Rust and leave C++ alone.
And ofc Google’s investment in Carbon
Everyone else outside the big three, is somewhere between C++14 and C++17.
> Relatively modern, capable tech corporations that understand that their code is an asset. (This isn’t strictly big tech. Any sane greenfield C++ startup will also fall into this category.)
and @bagxrvxpepzn is ofc
> Every ancient corporation where people are still fighting over how to indent their code, and some young engineer is begging management to allow him to set up a linter.
:)
Edit: except when interfacing with C APIs.
Win some, lose some though, as the overall development workflow is lightyears ahead of C++, mostly due to tooling
Rust decided to have more restrictive generic programming, with the benefit of early diagnostic of mistakes in generic code. C++ defers that detection to instantiation, which allows the generics to be more expressive, but it's a tradeoff. But this is an entirely different design decision to lifetime tracking.
This is true even for expressions that are only evaluated in a compile-time context, since dependently-typed languages do "everything" at compile time anyway, they don't have a phase distinction where you can talk about "runtime" being separate.
A truly dependently typed language performs these checks before instantiation time, by evaluating those expressions abstractly. Code that is polymorphic over values is checked for all possible instantiations, and thus its types can actually depend on values that will not be known until runtime.
The classic example is a dynamic array whose type includes its size- you can write something like `concat(vector<int, N>, vector<int, M>) -> vector<int, N + M>` and call this on e.g. arrays you have read from a file or over the network. The compiler doesn't care what N and M are, exactly- it only cares that `concat` always produces a result with the length `N + M`.
Rust already does have some level of const generic expressions, but they are indeed only possible to instantiate with values known at compile time, like C++.
The difficulty of type checking them symbolically still applies regardless of how they're instantiated, but OTOH it doesn't look like Rust is really trying to go that direction.
Notably Rust type-based generics do this, a key difference wrt. C++ templates. (You can use macros if you want checks after instantiation, of course.)
To be clear this distinction is not unique to dependent types, either. Most languages with some form of generics or polymorphism check the definition of the generic function/type/etc against the constraints, so the compiler can report errors before it ever sees any instantiations. This just also happens to be a prerequisite to consider something "dependently typed."
Typical implementations of dependent types do not generate a separate copy of a function for every instantiation, the way C++ does, so they simply do not need the concrete values in the same way.
My experience has been the other way around. Eclipse-based IDEs from NXP, TI, ST all have out-of-the-box usable tooling integration:
- MCU pinout and configuration codegen
- no need to manually fiddle with linker scripts
- static stack and code size analyzers (very helpful for fitting stuff in low-cost MCUs)
- stable JTAG-based debugging with:
- peripheral registers view (with bitfield definitions)
- RTOS threads view (run status, blocked on which resources, ...)
And yes, these are important enough for me to put up with Eclipse and pre-modern C/C++. I really want to write Rust for embedded but struggling with the tooling all the time didn't help.While this is sometimes done in C++ as well for various reasons, it's certainly not the default pattern there. If you have two things that need to point to each other, you just do that.
And then you have to handle all the subtle memory bugs that you've introduced by doing that.
> While programming in Rust, I've never thought to myself, "man, this would be so much easier to express in C++".
This is a concrete example of something that is much easier to express in C++. And, sure, you do pay the tax for that (although I will also dispute the notion that it is impossible to write C++ without memory bugs; it's just hard).
People think C++ is expressive because they think they are allowed to do a lot of things that they aren't, in fact, allowed to do in C++.
So why not be honest and just use C++01, or 11, or whatever it is that works for you, and let the rest of the ecosystem actually evolve and keep the language we invested so much effort into as a viable alternative? There's zero benefit, except to MS who want to sell this year's Visual Studio to all the companies with 80's-era C++...
It’s clear it’s imperfect. But not clear there is an obvious path to a nearby local maxima.
Design choices have tradeoffs.
And even if that were true, who would take advantage of that “better” language in a purely abstract sense? New language standards primarily exist to benefit existing C++ code bases, and the cohort of engineers who work on them. You have to consider that social reality.
I don't demand that C++ evolution be halted. I support the current trajectory of not adding viral annotations for the sake of implementing static lifetime checking. I want C++ to evolve into a better version of itself, I don't want it to become something it's not. If you want static lifetime checking, please use Rust. It already exists and it's great for people who need static lifetime checking.
If you care about program correctness in any real sense, memory safety is table stakes.
For simulations and scientific calculations, I do agree, to a vast extent. But in a world that is moving more and more towards zero-trust networking, even many of those will start being looked at as potential attack vectors into other systems.
This capability exists in completely open source, such as OpenZiti - https://openziti.io/.
That's why this topic is such a big deal. Even people who really should know better like the OpenZiti authors aren't able to reliably write safe code.
Now, you may say, "well, you have merely moved the listening port from the app to the overlay". Yes, true, not simple. Firstly overlay is written in Golang (thus memory safe). Secondly, if a vulnerability exists in the overlay network that would allow an attacker to bypass the security of the zero trust network, but what does that mean in practice? Well, to do this they would need to:
- bypass the mTLS requirement necessary to connect to the data plane (note, each hope is uses its own mTLS with its own, separate key). - strong identity that authorizes them to connect to the remote service in question (or bypass the authentication layer the controller provides through exploits... note again, each app uses separate and distinct E2E encryption, routing, and keys) - know what the remote service name is, allowing the data to target the correct service (not easy as OpenZiti provides its own private DNS that does not need to comply to TLDs, so it could literally be 'madeup.service.123') - bypass whatever "application layer" security is also applied at the service (ssh, https, oauth, whatever) - know how to negotiate the end to end encrypted tunnel to the 'far' identity
So yes, if they can do all that, then they'd definitely be able to attack that remote service. But I said "remote service", not "remote services". All that work and compromises and they only have access to 1 single service among hundreds, thousands, or potentially millions of services. Lateral movement is almost impossible. So the attacker would have to repeat each of the 5 steps for every service possible. Also, they don't know which company sits behind which OpenZiti fabric, so its pot luck if its even against the target they want to try and exploit.
Finally, we have developed a stateful firewall called 'ZitiFW' - https://github.com/netfoundry/zfw - which uses eBPF to look at the IP information of any incoming connections/packets to an Edge Router (Ziti's Policy Enforcement Point), if a connection/packet is received from an IP address which is not correlated to a known, bootstrapped endpoint to the overlay, the packet can be blackholed.
You see, it is absolutely expected and required that our applications will load and run arbitrary 3rd party code, generally with the expectation that it lives in the same address space as our application (though this is not formally required).
No sockets, no network, no backdoor hacks. You write code, call it a VST plugin, make it sound desirable ... we are expected to load and run it.
Yes, several DAWs have made the move toward out-of-process execution of plugins, but that doesn't begin to address the myriad problems caused by loosely-written plugin APIs not adequately pinning down threading, thread priority, memory access and more.
Filesystem access? Of course! That code runs as you! Because you want it to!
Plugins are not associated with attack vectors, even though they are literally just that.
"I don't want to install air bags and these shiny safety gadgets into my cars. We have been shipping cars without them for years and it works for us and our customers."
The problem is that it doesn't actually work as well as you think, and you are putting people at risk without realizing it.
As usual, the answer is quite simple: "please use rust". We promise to never mention when we break out nasm.
Driver anecdote: I have antilock brakes on my Tundra, but they are annoyingly counterproductive in 4WD descending 6" or larger sandy rocky steps. Do antilock brakes work overall best for the less capable mode? Of course! Do they work best for me? No.
Safety by default with escape hatches when absolutely necessary is the better way to go for all, even if it means some power users have to change their ways.
(Yes, I know about airbag vests. Let's analogize those with external static checkers.)
Of course it remains to be seen how this all plays out. Static lifetimes can be done good or bad. Profiles can be good or bad. Even if whatever we come up with is done well that doesn't mean people will (I know rust programmers who just put unsafe everywhere).
How can you know this without a "viral" analysis that tells you how much annotation is needed, and where? Perhaps the code factors out all the low-level, "memory unsafe" hacks to its own module, and that can be feasibly annotated. It's just not something we can know in advance.
While it is theoretically not impossible for that scenario to occur, I'd say it sounds wildly unlikely for anything that can be descried as 'old' code.
The fantasy is enough to get engagement and once you have engagement you can persuade people to do a "little" extra work to get the full benefits. My mother won't buy the product for $5, but if you tell her that it costs $10 but they're 2-for-1 today, she's going to buy that and feel like she got a bargain.
In terms of actually solving the problem well, it's not even captured in these hypothetical regulatory requirements. What you actually want is a safety culture, Rust has one, C++ does not, and no technology will change that. From what I can tell nobody at WG21 wants that to change anyway.
Rust has a safety culture because it involves requirements for Safe Rust that preserve safety while also playing well with modularity and iterative development. If "Safe C++" can enforce similar requirements, we can expect that a safety culture can be sustained there as well.
Yes a technology can be enabling, but, it isn't enough to inculcate the desired culture, that has to come from somewhere else. You can't "sustain" something which does not exist.
Actually WG21 ("The C++ Language Committee") illustrates this well in another way. When WG21 was created it was after the Mother Of All Demos, and so after video conferencing exists as an idea, but to be fair to them it was not really practical at the scale needed for WG21 processes at that time. When C++ 98 shipped it was just about practical, although most ordinary people would have needed to travel to some place with appropriate equipment. By this point the IETF is routinely but not yet universally using such technology.
By the time C++ 11 shipped, I have an ordinary job where I worked full time from home, travelling to a physical location only once or twice per month because video conferencing is now such a mudane and ordinary capability as to go unremarked.
Only since the COVID-19 pandemic has WG21 finally adopted the option for attendance without flying around the world several times per year. The technology to do this had existed for decades, but the culture did not exist.
The C++ leadership serves the C++ community, not the entire software industry. You and everyone who disagrees with them are free to use and write software based on other languages, e.g. Java and Rust.
The neat thing is that once the standard committee learns about this use case, it could get de facto support as existing use!
Which is why disabling RTTI, disabling exceptions, creating their own standard library replacement, static analysers forbinding specific language constructs, is such a big deal in some C++ circles.
Legislatures seem a lot more able to allocate large pots of money for major discrete projects than to guarantee an ongoing stream of revenue to a continuing project.
The profiles proposal suggests adding static lifetime checking, "without viral annotations." I use quotations because I don't really agree with this framing, but whatever. The paper is here if you'd like to read it yourself: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p30...
The core idea here is that you add annotations to opt in or out of certain checks. And opting in may be a compiler flag, requiring no changes to source code. So that would be "backwards compatibility" in that sense. Of course, code may fail these checks, so you'll have to add annotations to opt out, or re-write the code. We will see in practice how much change is required once implementations exist and are tried out.
But the other part is, these profiles do not attempt to cover all valid cases. And what I mean by that is, there are some lifetime issues that this proposal does not attempt to analyze. And, where the analysis is similar, they offer a subset of what other proposals do. These decisions were made because the authors believe that they'll reduce a significant number of issues, and are easier to adopt. And that's worth it instead of going for more checks.
The competing proposal, Safe C++, has you opt into safety checks on a file-per-file basis. So in that sense, it is also backwards compatible: all existing code compiles as-is. When you opt in to those checks, it adds new syntax, similar to Rust, to do the safety analysis checks. So you gain this benefit for only new code, but it also you get much more power. This syntax is necessary to communicate programmer intent to the checks, but is the "viral annotations" that the proponents of profiles don't like.
So, basically, that's the thing: both are backwards compatible, but offer very different tradeoffs in the design space.
If having strict safety means I can't express my mental models in code, I don't want it. It will slow me down. It will make it harder to write software that's useful.
Remember people, we are here to make things that are useful to people. If safety gets in the way of that, then it's not worth it.
But this isn't one thing. Part of the problem is contradictions even in the definition of this trajectory. An important example: Will it be "you don't pay for what you don't use", or will it be "stable ABI"? There are adopted papers which say both - as the linked article indicates.
> please just consider another language, e.g. Rust
I'm not one of those people necessarily, but - Rust has its own set of design goals and problems. It is legitimate to want C++ to go one way rather than another.
> To Rust advocates, you can have the US government and big tech.
Now you are kind of contradicting yourself, because wide applicability is definitely a goal for C++.
This code was originally developed in the late 1980s.
A good packaging tool would have helped a lot.
When I contributed to LibreOffice (GSoC 2012) they were still on C++03 !
Ha ha ha, that's funny. It uses pre-98 C++ code, that's set in stone because of extension/UNO APIs. Yes, you can use C++17 in a bunch of places, but not for the basic structures, classes, idioms etc.
And - that's coming from a huge LibreOffice supporter. Speak at conventions, got the T-shirts, everything.
Yes, languages that are beginner friendly are ... friendlier. Yes, languages that stick to one or a small number of programming paradigms are friendlier. But if you want the "flexible efficiency and raw power of C" and "something higher level than C", C++ is your baby.
Maybe it would be better if we all used Java, Rust, and Go, but C++ sings its siren von Neumann song to the wizards, and there will always be wizard musicologists who steer their projects toward those rocks and, when they have just enough wax in their ears, they sail right past the rocks and come out the other side of the straits leading the rest of the fleet.
You can choose to follow them or not, for there's no shame in coming in 4th.
Feel the committee was smoking weed that day in la-la land. You can ignore all the safety stuff from Sean Baxter, but saying no to performance on the altar of permanent, un-specified ABI backward compatibility - when such was never mentioned as a design goal of C++ - means its "Goodbye C++" for a long, long list of orgs and "wizards". The ABI was NEVER specified formally by the C++ standard - so why bother sacrificing the world for its immortal existence ?
C++ is NO longer the choice of language for greenfield native projects and the committee takes the full blame.
I worked in a previous role on C++ CAD/simulation software that required vendored things like solid modelling kernels and it was incredibly painful. Occasionally one of the vendors would just not do the work and you'd end up having to spend half a year ripping out the dependency that worked perfectly well. The team working on the software were generally in favour of moving up through to modern standards, while I was there we did 03 -> 17 for e.g. but that didn't finish til 4 years after the C++17 standard came out for all sorts of reasons. When VS2017 came out everyone breathed a sigh of relief because suddenly we didn't have to wait to upgrade the compiler.
C++ was always by far the most inefficient langauge to work with for me, because there's just so much chore and nonsense that you have to get through to get anything done, and almost none of it has any reasonable purpose, there's no efficency tradeoff. I'm pretty sure that the insane build situation or UB in uninitialized variables or unspecified argument evaluation order never really benefited anybody, they are just bad decisions in the language, and that's all.
You will be happy to learn the uninitialized variables are not UB as of C++26.
beautiful, in equal parts true, sad, and endearing.
but also remember the vasa.
You don't want to depend on a third-party hosting the code, so you need to copy it, and pin it to a specific version. You might also need to patch it since you'll be using it and most likely will run into problems with it.
Using third-party code means taking ownership of all the problems one might encounter when trying to use it in your project, so you might as well just adapt it for it to work with your tools and processes.
If you use a modular system this is essentially just maintaining a fork.
Oh I see, this is a fantasy.
It's all about the magnitude of the liability, not the direction
You do not need to re-solve this problem, and when a similar problem occurs, you can adapt the existing solution to the new problem.
Another way to think about it: if code was not an asset, we would delete it immediately after compilation.
Meanwhile, most of the C++ code from Google seems to be written in some mishmash of different ideas, always at some halfway point along a migration between something ancient and something passable... but never anything I would ever dare to call "modern", and thereby tends to be riddled with state machines and manual weak pointers that lead to memory corruption.
So... I really am not sure I buy the entire premise of this article? Honestly, I am extremely glad that Google is finally leaving the ecosystem, as I generally do not enjoy it when Google engineers try to force their ridiculous use cases down peoples' throats, as they seem to believe they simply know better than everyone else how to develop software.
Like... I honestly feel bad for the Rust people, as I do not think the increasing attention they are going to get from Google is going to be at all positive for that ecosystem, any more than I think the massive pressure Google has exerted on the web has been positive or any more than the pressure Google even exerted on Python was positive (not that Python caved to much of it, but the pressure was on and the fact that Python refused to play ball with Google was in no small part what caused Go to exist at all).
(FWIW, I do miss Microsoft's being in the space, but they honestly left years ago -- Herb's existence until recent being kind of a token consideration -- as they have been trying to figure out a tactical exit to C++ ever since Visual J++ and, arguably, Visual Basic, having largely managed to pivot to C# and TypeScript for SDKs long ago. That said... Sun kicking Microsoft out of Java might have been really smart, despite the ramifications?)
> riddled with state machines
Why is this bad? Normally, state machines are easy to reason about.Imagine you have an informally-specified, undocumented, at-least-somewhat-incomplete state machine. Imagine that it interacts with several other similar state machines. Still easy to reason about?
Now add multithreading. Still easy?
Now add locking. Still easy?
Cleanly-done state machines can be the cleanest way to describe a problem, and the simplest way to implement it. But badly-done state machines can be a total mess.
Alas, I think that the last time I waded in such waters, what I left behind was pretty much on the "mess" side of the scale. It worked, it worked mostly solidly, and it did so for more than a decade. But it was still rather messy.
In general, state/event machine transition table and decision table techniques of structuring code are easier to comprehend than adhoc and even worse, poorly understood pattern-based techniques are.
You think that developers that wrote an informally-specified, undocumented, at-least-somewhat-incomplete state-machine would have written that logic as a non-state-machine in a formally-specified, documented and at-least-somewhat-complete codebase?
State-machines are exceptionally easy to reason about because you can at least reverse-engineer a state-diagram from the state-machine code.
Almost-a-state-machine-but-not-quite are exceptionally difficult to reason about because you can not easily reverse-engineer the state-diagram from the state-machine code.
Want to add another "bool state"? Hello exponential growth...
> Now add locking. Still easy?
Don't do that then.
Or rather, either manipulate the state machine from only a single thread at a time; or explicitly turn the multithreading into more states. If you need to wait for something instead of having "do X" you transition into state "doing X". C#-style async does this in a state machine behind the scenes.
What do you suggest instead of a state machine?
If needs be the state-machine can be reconstructed on a whiteboard by a team of five.
> especially any that emerge from a codegen tool
Can you give an example?State machines often are implemented with mutable objects.
And one does not need mutable objects to make "modules-> objects with the transitions being method calls". Every method call could return a fresh, immutable object, nothing requires mutation there.
I'd see a method like:
`TransitionTo(newState)`
as a major smell compared to an explicit
`TransistionToNewState`
and I think OOO can be helpful (hardly required, of course) in that one neat way of streamlining usage of your code is that if you're implementing objects then the object for something in "State A" might not even have "TransitionToStateC" if that's not a valid operation.
(No, you don't HAVE to write state machine code that allows you to ask for invalid operations, but it's a common pattern I've seen in real code and in online discussion/blogs/stack overflow.)
For a state machine, I would expect a function such as:
transition : (old_state, event) -> new_state
Or if we use immutable objects, and one method per simple event, then something like:
transition_event1 : () -> new_state
Which I think is similar to what you hace. So I think we are in agreement here.
I definitely was not: I would argue for structured logic rather than implicit state. The idea you are discussing seems to be more about imperative vs. functional design, and that would also be a lot better... but these are Google engineers managing a million interacting state machines via a giant pile of global (mutable) state, resulting in tons of bugs such as this one that isn't even fixed yet:
https://issues.webrtc.org/issues/339131894
A reply I just left elsewhere on this thread, noting that "state machine" doesn't imply the clean perfectly-factored concept you'd learn in a computer science class: these are ad hoc state machines that result from having a number of developers who don't really care and who think they can "dead reckon" the state of the system if they just add enough transition functions.
Yeah that doesn't sound good. I understand the point you are making now and agree.
Now, if we said to someone to model a weather prediction via state-machine- that would be obvious madness. But if we take a small object (like a cubic meter of air) and modelled that part to handle inputs and transfer forces, that generic statemachine will do the job- because the atmospheric ocean of statemachines knows more about the whole system, than a single statemachine.
My point is- there is state in a system, that is not explicitly modeled.
My rough rule of thumb based on experience is that if the state machine being a state machine is visible outside of it's internal implementation (compared to just an interface with operational methods that don't hint at how things are managed behind the scenes) it's probably too leaky and/or incomplete.
I would trust code with extensive state-transition testing (regardless of internal implementation) - I wouldn't trust code that claimed to implement a state machine and didn't have that testing, or extensive documentation of edge cases and what was left out of the state machine.
As a simple example of something that's often left out in a way that fucks up state machines: the passing of time.
I'll use a state machine!
Now, I have two problems :-(
It doesn't matter if they have equivalent power. One of those representations fundamentally allows your software to have an architecture, the other doesn't.
Let's look next at that "compiler" thing and high-level languages. The hardware-native one suffices, no need for all that bloat.
Attempting to understand every state and edge case before writing code is a fool's errand because it would amount to writing the entire program anyway.
State machines are a clear, concise, elegant pattern to encapsulate logic. They're dead simple to read and reason about. And, get this, writing one FORCES YOU to fully understand every possible state and edge case of the problem you're solving.
You either have an explicit state machine, or an implicit one. In my entire career I have never regretted writing one the instant I even smell ambiguity coming on. They're an indefatigable sword to cut through spaghetti that's had poorly interacting logic sprinkled into it by ten devs over ten years, bring it into the light, and make the question and answer of how to fix it instantly articulable and solvable.
I truly don't understand what grudge you could have against the state machine. Of all the patterns in software development I'd go as far as to hold it in the highest regard above all others. If our job is to make computers do what we want them to do in an unambiguous and maintainable manner then our job is to write state machines.
It doesn't force you to do that at all.
You can start piling in hacks to handle edge cases inside of certain states, for instance, instead of splitting them into their own states. Or the next dev does.
Now it's an implicit ball of mud that pretends to be something else and has a execution pattern that's different from the rest of your company's business logic but not actually strictly "correct" still or easier to reason about for the edge cases.
And that's what most people do. They don't use it as a tool to force them to make things unambiguous. They bail when it gets hard and leave crappy implementations behind.
Copy-pasting from another reply to a different comment: As a simple example of something that's often left out in a way that fucks up a lot of devs' attempts at state machines, and is super annoying to draw in a typical state diagram: the passing of time.
I actually use passage of time as a factor in state machines all the time on game dev projects. It's pretty simple, just store a start time and check against it. I don't see how "ten seconds have passed since entering state A" is a more difficult condition than any other to draw or model.
For business-logic reasons, where I've generally seen it fall apart is when things go from fairly simple things like "after six months of inactivity this account is considered idle" to more complex interactions of timers and activity types. "Move fast and break things", "just get to MVP" attitudes rarely have the discipline to formally draw all the distinct states out as the number of potential states starts to exceed a couple dozen.
lol? ;P Here is just one example of a bug I know of that should exist, today, in Chrome, because, in fact, state machines are extremely hard to reason about. (I have filed previous ones that were fixed, and tons of bugs in Chrome in general are in this category. This one is top of mind as they haven't even acknowledged it yet.)
https://issues.webrtc.org/issues/339131894
Now, you are going to tell me "they are doing state machines wrong as they don't have a way to discriminate on what the state even is in the first place"... and yet, that's the problem: the term "state machine" does not, in fact, mean a very narrow piece of inherent algorithmic limitations any more than "regular expressions" implies the language is regular, as this is an engineering term, not a computer science one.
In the field, state machines just kind of happen by accident when a ton of engineers all try to add their own little corner of logic, and the state is then implied by the cross-product of the state of every variable manipulated by the edges of the machine. This results in a complete mess where, in fact, it is essentially impossible to prove that you've provided edges for every possible state. Nothing in the type system saves you from this, as the state isn't reified.
This contrasts with approaches to these problems that involve more structured concurrency, wherein the compiler is able to not only deduce but even some concept of what kinds of edges are possible and where the state lies. (This, FWIW, is a reason why async/await is so much preferable to the kind of callback hell that people would find themselves in, maintaining a massive implicit state machine and hoping they covered all the cases.)
Downside of course is now you have a dependency on qt.
If you can afford to do things like this you can most likely use something other than C++ and save yourself a lot of headaches.
Surely you can understand that, despite the recent c++ hate, my job doesn't give a fuck and we aren't migrating our massive codebase from c++ to... anything.
I place a note at the top of my diagrams stating what the default state would be on receipt of an unexpected event. There is no such thing as "event silently gets swallowed because no transition exists", because, in implementation, the state machine `switch` statement always has a `default` clause which triggers all the alarm bells.
Works very well in practice; I used to write hard real-time munitions control software for blowing shit up. Never had a problem.
Ha, Ha, Ha! The juxtaposition of these two phrases is really funny. I would like to apply for a position on the Testing team :-)
It had its moments: used to go to a range where we'd set off detonators. Once or twice in production on site where we'd set off actual explosives.
Well, you may be celebrating a bit prematurely then. Google still has a ton of C++ and they haven't stopped writing it. It's going to take ~forever until Google has left the C++ ecosystem. What did happen was that Google majorly scaled down their efforts in the committee.
When it comes to the current schism on how to improve the safety of C++ there are largely two factions:
* The Bjarne/Herb [1] side that focuses on minimal changes to the code. The idea here is to add different profiles to the language and then [draw the rest of the fucking owl]. The big issue here is that it's entirely unclear on how they will achieve temporal and spatial memory safety.
* The other side is represented by Sean Baxter and his work on Safe C++. This is basically a whole-sale adoption of Rust's semantics. The big issue here is that it's effectively introducing a new language that isn't C++.
Google decided to pursue Carbon and isn't a major playing in either of the above efforts. Last time I checked, that language is not not meant to be memory safe.
[1] https://github.com/BjarneStroustrup/profiles [2] https://safecpp.org/draft.html
Carbon is an experiment, that they aren't sure how it is going to work out in first place.
> "If you can use Rust, ignore Carbon"
https://github.com/carbon-language/carbon-lang/blob/e09bf82d...
> "We want to better understand whether we can build a language that meets our successor language criteria, and whether the resulting language can gather a critical mass of interest within the larger C++ industry and communit"
https://github.com/carbon-language/carbon-lang/blob/e09bf82d...
Herb is developing a whole second syntax, I wouldn't call that minimal changes. And probably the only way to evolve the language at this point, because like you said Sean is introducing a different language entirely, so its not C++ at that point.
I really like some of Herb's ideas,but it seems less and less likely they'll ever be added to C++
He compares it to the JS/TS relationship.
typescript
function add(a: number, b: number): number { return a + b };
javascript function add(a/*: number*/, b/*: number*/)/*: number*/ { return a + b };
cppfront add: (a: float, b: float): float = { a + b; }
cpp float add(float a, float b) { return a + b; }
You can see where CPPFront inserts a `cpp2::move` call automatically, and how that differs from a superficially equivalent Cpp1 function.
Is this true in the general case? I thought there were typescript features that didn't have direct JavaScript alternatives, for example enums.
So, yes, you can't just strip types, but it's close.
That guarantees that the types do not determine the output (e.g. no const enums), not that you can "strip" types to get the same output.
Decorators would be another example. (Though they have always been marked experimental.)
And of course JSX, but that's not a TypeScript invention.
He also sells the language differently from any other language that also compiles to native via C++, like Eiffel and Nim among others, due to conflict of interest to have WG21 chair propose yet another take on C++.
OP is right, TypeScript is a whole new syntax, and it's shtick is that it can be transpiled into JavaScript.
He at least claims that Carbon will have memory safety features such as borrow checking down the line. I guess we'll see.
The entire philosophy errs too much in the direction of “being reasonable” and “pragmatic” while getting fundamental things wrong.
> Over time, safety should evolve using a hybrid compile-time and runtime safety approach to eventually provide a similar level of safety to a language that puts more emphasis on guaranteed safety, such as Rust. However, while Carbon may encourage developers to modify code in support of more efficient safety checks, it will remain important to improve the safety of code for developers who cannot invest into safety-specific code modifications.
That’s really just paying lip service to Rust without recognizing that the key insight is that optional memory safety isn’t memory safety.
It is kind of neat just how much Rust has managed to disrupt the C++ ecosystem and dislodge its position.
Java guarantees VM integrity in the face of data races, while for example many races are UB in theory and in practice in Go. Both are considered safe languages.
Sometimes pragmatism is in fact a valid goal.
edit: from a practical point of view I don't know how realistic is to retrofit memory safety to a language that lacks it.
I could just as easily offer a valid counter analysis to explain the data. They’re “hard” just because there’s so many easier avenues so attackers just often don’t bother not because they’re intrinsically unlikely. Let’s say you’re successful in eliminating temporal & spatial classes of failures completely (even Rust proponents do not claim this). They’ll focus on data races and type confusion next.
> Both are considered safe languages
Go is considered a memory safe language today because C and C++ are the anchor that we compare against and we have overwhelming evidence against it (but it’s also where a huge amount of value is in terms of the systems they underpin). In 50 years time, it’s not inconceivable that a lot of the languages may lose their memory safety designation if their runtimes continue to be written to C/C++ (Java) and/or data races remain unaddressed at the language level (Go) and we have overwhelming evidence that exploits just moved on to architectural defects in those languages.
It may raise costs of exploits but cybercrime is estimated to be a 10T dollar market next year so there’s clearly a lot of money to put towards exploits.
> from a practical point of view I don't know how realistic is to retrofit memory safety to a language that lacks it
I think letting people evolve into a more memory safe situation is good. I think doing it partially instead of tackling memory safety in all its forms is just asking for trouble - your attackers will be able to develop exploits more quickly than you are able to update all existing code to more secure language features.
Carbon is intended to be memory safe! (Not sure whether you intended to write a double negative there.) There are a few reasons that might not be clear:
* Carbon has relatively few people working on it. We currently are prioritizing work on the compiler at the moment, and don't yet have the bandwidth to also work on the safety design.
* As part of our migration-from-C++ story, where we expect code to transition C++ -> unsafe Carbon -> safe Carbon, we plan on supporting unsafe Carbon code with reasonable ergonomics.
* Carbon's original focus was on evolvability, and didn't focus on safety specifically. Since then it has become clear that memory safety is a requirement for Carbon's success, and will be our first test of those evolvability goals. Talks like https://www.youtube.com/watch?v=1ZTJ9omXOQ0 better reflect more recent plans around this topic.
The double negative was not intended :)
I feel like if I'm gonna go through the whole nightmare of a code port I should get something for it as opposed to just relying on interop
Hence the failure of Longhorn, or any attempt coming out from Microsoft Research.
Ironically, given your Sun remark, Microsoft is back into the Java game, having their own distribution of OpenJDK, and Java is usually the only ecosystem that has day one parity with anything Azure puts out as .NET SDK.
Two quite common names in the Microsoft ecosystem.
Apparently they have a programming language for which you can "one-click-switch" between english and french for the keywords??? https://pcsoft.fr/windev/ebook/56/
https://www.zdnet.com/article/microsoft-splits-up-its-xaml-t...
If I'm stretching this "windev" thing, the domain for a lot of employee accounts (including mine) was NTDEV, that had a longer history afaik, nobody called an org that..
I didn't come up with this definition myself.
If I am not mistaken, I can probably even dig some Sinosfky references using it.
https://github.com/microsoft/openjdk-aarch64
https://www.infoq.com/news/2023/02/microsoft-openjdk-feature...
I spilled my coffee, I was just talking the other day to some coworkers how I don't trust google open source. Sure they open their code but they don't give a damn about contributions or making it easy for you to use the projects. I feel a lot of this sentiment extends to GCP as well.
So many google projects are better than your average community one, but they never gain traction outside of google because it is just too damn hard to use them outside of google infra.
The only Google project that seems to evade this rule that I know of is Go.
Nix build is still stuck in the one from 3-4 y back because bazel doesn't play well. Debian too has some issues building the thing...
Otherwise I agree, because if you must be careful, you might as well use tooling that's built for such care. But if you're doing that, do you need the Dockerfile? And that's how you end up with nix/guix.
For the humans, we can render the hashes as something friendly, but there's no reason to confuse the machines with our human notions of friendliness.
It's not like python will let you:
import nix.numpy-hsbdjd...8r5z2 as np
Such that the import mechanism ensures that the correct build of numpy is used.For that to work you'd have to change nix such that the hash did not digest parameters like `amd64-linux` witch indicated the system architecture (you'd want those to be satisfied at import time).
It's python packaging and the way the only really supported binary distribution method of Tensorflow for many many years was to use Pip and hope it doesn't crash. And it's reflected in how the TF build scripts only support building python lib as artefact, everything else at the very least involved dissecting bazel intermediate targets
I still go back and forth on whether google test and mock are worth it.
Google benchmark is also nice.
honestly if you write C++ for work, there's no excuse for your company to not give you the beefiest dev machine that money can reasonably buy. given that rust exists, I think "get a faster computer" is a totally valid answer to build times, especially now that skylake malaise era is over and CPUs are getting faster
I find this amusing because one of the main reasons i avoid Rust (in the sense that i prefer to build things written in other languages if possible - i don't mind if someone else uses it and gives me a binary/library i can use - and it never went beyond "i might check this at some point, sometime, maybe" in my mind) is the build times compared to most other compilers :-P.
Also, at least personally, if i get a faster computer i want my workflow to be faster.
I've given up on mock frameworks. They make it too easy to make an interface for everything and then test that you are calling functions with the expected parameters instead of the program works as you want. A slight change to how I call some function results in 1000 failed tests and yet I'm confident that I didn't break anything the user could notice (sometimes I'm wrong in this confidence - but none of the failing tests give me any clue that I'm wrong!)
Mocks have their place. A prototypical example is at user-visible endpoints (eg: a mock client).
I'm not arguing that mocks don't have their place. However I have found that by declaring I won't use them at all I overall come up with better solutions and thus better tests.
If a team of people who have been SWEs for decades reports that something helped their team, and you try it and it doesn't work, and you have been SWEs for decades, that doesn't automatically mean they are charlatans selling nonsense. They might all be basketball players playing together for 5 years and you might be a team of a baseball player, a racecar driver, a track and field athlete, and a water polo player, trying to play basketball from only reading about it, with nobody who has done it or experienced it, and several people who quietly don't want to be playing it and are just nodding along while hoping it fails. The conclusion that they are liars and it can't possibly work is not a strong conclusion.
Especially if you have a build process that always runs your unit tests, it's nice to have a very fast test/compile/debug loop.
Also, IMO, both doctest and catch2 are far superior to Google Test.
I feel like you could make a madlib where you could plug in any two project names and this sentence would make sense.
1) Databases and other persistent storage. Though in this case, the best mock for a database is generally another (smaller, easily snapshottable) database, not something like googlemock.
2) Network and other places where the hardware really matters. Sometimes, I really want to drop a particular message, to exercise some property of the sender. This is often possible to code around in greenfield projects, but in existing code it can be much simpler to just mock the network out.
3) Cases where I am calling out to some external black-box. Sometimes it's impractical to replicate the entire black-box in my test. This could be e.g. because it is a piece of specialized hardware, or it's non-deterministic in a way that I'd prefer my test not to be. I don't want to actually call out to an external black-box (hygiene), so some kind of a mock is more or less necessary.
That makes sense internally for Google because they have their massive monorepo, but it sure as hell makes it a pain in the ass to adopt for everyone else.
Google open source libraries are often a mess when you try to include more than one of them in the same project, but googletest isn't an example of the mess. It's actually pretty straightforward.
Completely agree. In isolation all of their libs are great, but inevitably I end up having to build Abseil from source, to then build Protobuf off of that, to then build gRPC off of that. If I can include the sanitizers under Google then that also becomes painful because Abseil (at least) will have ABI issues if it isn't built appropriately. Thinking about it I'd really just like a flat_hash_map replacement so I can drop Abseil.
boost has a flat_hash_map implementation for quite a few versions now, which from what I could see generally beat or is competitive with the absl implementation: https://www.reddit.com/r/cpp/comments/yikfi4/boost_181_will_...
I was curious what exactly differentiates boost::unordered_flat_map from absl::flat_hash_map, and was not disappointed. It seems that the lion's share of the performance improvement comes from using more of the metadata for the reduced hash value, although there are a few other contributing factors.
The blog post further describes where absl::flat_hash_map performs better: iteration (and consequently erasure), which is ironic given those are a couple of areas where I always felt that absl::flat_hash_map was especially weak. But, it makes sense to double down on Abseil's strengths as well as its shortcomings.
https://bannalia.blogspot.com/2022/11/inside-boostunorderedf...
https://github.com/boostorg/boost_unordered_benchmarks/tree/...
I especially like how you can see the load factor across the graphs, where there are sharp downward spikes each time the map resizes, and how they vary as you move through the memory hierarchy.
I am curious what Abseil could learn from other modern hash map implementations, since my understanding is that the fundamental structure of its swisstables implementation hasn't changed meaningfully since 2017.
Looking at the build configuration code:
https://github.com/google/googletest/blob/main/CMakeLists.tx...
it seems like the dependence on Abseil is optional. i.e. you can use googltest on its own. I wouldn't recommend it (I kinda like doctest), but still.
Here is a concrete reason why Google open source sucks when it comes to contributions and I don't think it can be improved unless Google changes things drastically: (1) an external contributor makes a nice change and a PR on GitHub; (2) the change breaks internal use cases and their tests; (3) the team is unwilling to fix the PR or port the internal test (which may be a test several layers down the dependency tree) to open source.
> making it easy for you to use the projects
Google internally use Blaze, a version of Bazel. It's so ridiculously easy for one team to use another team's project that even just thinking about what the rest of us needs to do to use another project is unloved dreadful work. So people don't make that effort.
I do not see either of these two points changing. Sure there are individuals at Google that really care about open source community, but most don't, and so their project is forever a cathedral not a bazaar.
In the case of KHTML, they never used it in the first place, so it seems like a particularly inappropriate example. I assume you actually meant Webkit? In that case, they spent half a decade and thousands of engineer-years contributing to Webkit, so it doesn't fit the original complaint about not "trying to contribute" either.
November 4, 1998; 26 years ago (KHTML released)
June 7, 2005; 19 years ago (WebKit sourced)
https://chromium.googlesource.com/chromium/src/+/HEAD/third_... * (C) 1999-2003 Lars Knoll ([email protected])
* (C) 2002-2003 Dirk Mueller ([email protected])
* Copyright (C) 2002, 2006, 2008, 2012 Apple Inc. All rights reserved.
* Copyright (C) 2006 Samuel Weinig ([email protected])
"...they never used it in the first place"AFAIK Google did not take ownership of gcc, nor did they try to fork it without contributing to the original. They used GCC for a good couple of decades while contributing to it, but eventually switched to a different compiler. The same for clang, they neither "took it over" nor "forked it without trying to contribute".
That's... not even close to what happened?
Historically, LLVM was at one point proposed by Chris Lattner, while he was at Apple, to be upstreamed into GCC (and relicensed to GPL, natch) for use as at the LTO optimization phase, which was declined. For most of its early existence, it used llvm-gcc as the frontend to generate LLVM IR. In the late '00s, serious effort was put into making a new frontend for LLVM IR which we know as clang, primarily by Apple at that point, which become self-hosting in 2009 or 2010. Basically the moment clang becomes self-hosting, everyone jumps ship from using llvm-gcc to using clang to make LLVM IR.
Google shows up around this time, I think primarily motivated by the possibility that Clang offered for mass rewriting capabilities, since it has extraordinarily good location tracking (compared to the other compilers available), which is necessary for good rewriting tools. The other major area of Google's focus at this time is actually MSVC compatibility, and I distinctly remember Chandler talking in one of his presentations that you need to be able to compile code to trust it well enough to rewrite your code, so I think the compatibility story here was mostly (again) for rewriting.
Also around this time, gcc gains proper plugin support, and llvm-gcc is reworked into dragonegg to take advantage of the proper plugin support. But because clang now exists, dragonegg is no longer very interesting, with almost all the residual attempts to use dragonegg essentially being limited to people trying to use it to get LLVM IR out of gfortran, as LLVM had no fully-working Fortran compiler at that point.
Ars is controversial on YC news. Who knew?
I don't see a problem here. Why should google have to deal with the opinions of a maintainer if they can just maintain their own version. Yeah obviously it would be nice if they'd contribute their changes back to the upstream repo but from a business perspective it's often not worth it.
At my company the inverse of this problem happened way more often: We find a problem but the maintainer just doesn't care. For example the backward-cpp library is a good example where the maintainer just isn't that active in the issues. Why wait for him to respond if you can just fork it and keep on moving.
Personally, I take the cathedral/bazaar distinction to indicate different development cadences and philosophies, rather than whether contributions are allowed/encouraged.
Various cathedral-style projects (eg: FreeBSD, Emacs) still actively take contributions and encourage involvement.
There's something even further along the spectrum that's "we provide dumps of source code, but don't really want your patches." I'm not sure what the best term is for that, but "source [merely] available" sometimes has that connotation.
In fact "source available" usually means you can see the source code, but there are severe restrictions on the source, such as no permission to modify the source even for your own use, or no permission to create forks of the project containing the modifications, or severe restrictions on such modifications. An example is MongoDB's Server Side Public License, which is source-available but not open source.
And when they don't when talking about source code, they are wrong. If someone says that an RJ45 cable is "a piece of software" because it's "soft" (you can bend it), would you say it's just a different perspective?
Open source, in the context of software, has a particular meaning. And it is the case that many software developers don't know it, so it's worth teaching them.
I agree that the OSI meaning is worth teaching. But perhaps not by saying "you're wrong; there is only one right way." Perhaps more like "some people attach XYZ specific meaning to that phrase, please be aware of it. Also, here is some history of the term if you like."
----
Aside: On re-reading this, I wonder if it comes across as testy... I think I am just channeling my annoyance with the language police of the world, in general, who sour people's interest in topics with their gatekeeping behavior. I don't mean it too personally towards you (:
> One could ask whether Google works ‘open source’ or more ‘source available’; the source is there but you cannot contribute, if you can build it at all
The author of this comment says "if you can't contribute, shouldn't you consider it `source available` instead of `open source`?".
There is only one valid answer: "No, you should not. It is still open source even if you cannot contribute". The context is clear, we are talking about "open source" vs "source available", which are both very specific in this context.
> I think I am just channeling my annoyance with the language police of the world, in general, who sour people's interest in topics with their gatekeeping behavior. I don't mean it too personally towards you (:
No offense taken, and I don't mean it personally either =). My point is just that in this context, the author of the comment was pretty clearly talking (asking, even?) about the difference between "open source" and "source available".
I don't even think it's shutting down the author: there was no other point than this, so the "thread" started by this author was purely about the meaning of those words.
Inevitably, a laser-minded person talks with a sloshy-bucket person and misunderstandings ensue.
In sloshy-bucket land, I think "open source" has various connotations — a sense of community, encouraged contribution, being able to build it yourself, improve it yourself, etc.
And I think the commenter, in broad strokes, was saying that Google is not upholding those various virtues that are often associated with "open source," so felt the term was not a good (sloshy) fit.
In particular, I do not think they were asking the question you say they were asking.
In this space, it seems like there are both too many terms (so people rather just pick a popular one and over-apply it) and too few (so you can never find one that quite says what you want). Such is life, I guess. Maybe "open sourcey" would be good, to indicate it's talking about a hand-wavy vague "ness" rather than a particular nailed-down definition. "Google isn't being very open sourcey"? ¯\_(ツ)_/¯
Anyway, all this to say: in the ethos of trying to take a charitable interpretation of people's words, I think it's good to consider the bucket-of-paint possibility, before jumping to corrections and yes/no determinations.
----
Edit: It occurs to me that originally I misinterpreted you as being persnickety, when perhaps you were just trying to answer the question you felt they had asked. Sorry!
> And I think the commenter, in broad strokes, was saying that Google is not upholding those various virtues that are often associated with "open source," so felt the term was not a good (sloshy) fit.
Totally valid! And I like the idea of considering the "bucket-of-paint" possibility before saying "no you're wrong". But on the other hand, sometimes it's worth agreeing on the meaning of words while discussing something.
I feel like I actually happen to regularly be on the bucket-of-paint side. I will often simplify the part of the discussion that I feel is not relevant by saying e.g. "okay this solution is bad, so if we look into this other solution we have to think about ...". And sometimes people really care about starting a discussion saying "by saying it's bad, you make it sound like whoever would think about it is stupid, and that's extreme. This solution is not necessarily bad, because in some situations it may work even though it is suboptimal". To which I tend to say "sure, I said it was bad as a way of saying that we seemed to agree that we would focus on the other one".
Until this point it's perfectly fine for me. What frustrates me is when the discussion continues in what I feel sounds like, e.g. "no, I think that your saying it is bad reflects that you disrespect whoever would think about it, and you should never have used that word in the first place. I am not sure I can ever have a meaningful discussion with you now that you used this word in this sentence, even if you later admitted that it was an oversimplification".
Anyway, communication is hard :-)
You could also build and run it using completely standard tools; you didn’t need to download random internal source control software etc. like you do for e.g. Chromium.
I assume there is little will internally because everyone there is so focused on their performance reviews and helping external people using google open source projects is not tracked by that.
React must've been destined to be open source from the get go: gotta create a mountain of js to hide in so the users can't strip out the malicious parts. Kubernetes on the other hand could've been internal forever and still would've made sense. It just happened to later make sense to open source it (it feels lopsided to me, like they kept certain parts secret. It wouldn't feel that way if they had planned it as OSS from the get go).
What's wrong with state machines? Beats the tangled mess of nested ifs and fors.
Interestingly, Google has fired the Python team this year. The revolution eats its own?
Anyway, Rust should take note and be extremely careful.
Some years ago Google decided that Go projects were similar engineering effort, better performance, lower maintenance, and so on that basis there was no reason to authorise new Python software and their existing projects would migrate as-and-when.
Google can embrace modern processes, but the language itself had better be compilable on whatever ancient version of gcc works on the one mission-critical architecture they can't upgrade yet...
I'm impressed that you even get as far as finding out whether that much C++ from disparate sources works on a newer version of C++. The myriad, often highly customized and correspondingly poorly documented build systems invented for each project, the maze of dependencies, the weird and conflicting source tree layouts and preprocessor tricks that many projects use... it's usually a pain in the neck to get a new library to even attempt to build, let alone integrate it successfully.
Don't get me wrong, we use C++ and ship a product using it, and I occasionally have to integrate new libraries, but it's very much not something I look forward to.
We are just now feeling this. Some original contributors left the field, and lately the language has went in directions I don't agree with.
The kind of mass refactorings / cleanups / static analysis talked about in this article are done on a much more serious and large scale on C++ inside the Google3 monorepo than they are in Chromium. Different build systems, different code review tools, different development culture.
I work full-time in Rust these days and everytime I go back to working in C++ it's a bit of a cringe. If I look long enough, I almost always find a use-after-free, even from extremely competent developers. Footgun language.
I don’t know if they’ve really abandoned C++ entirely—the compiler team certainly hasn’t, that’s true. But the above doesn’t feel like first-class support.
[1] https://github.com/microsoft/cppwinrt/issues/1289#issuecomme...
[2] https://learn.microsoft.com/en-us/dotnet/core/deploying/sing...
C#/dotnet continues nicely, but the team is surprisingly small if you look closely.
https://developercommunity.visualstudio.com/t/Implement-C23-...
I suppose usually one would like to have everything from a language standard.
The C++20 winning run seems to have been one of a kind, and whatever made it possible is now gone.
Speaking of gone, Herb Sutter has left Microsoft and most certainly had to do something with whatever is going on MSVC, C# improvements for low level coding, and Rust adoption.
One thing I learned, for example, is do not access global immutable state from within a function. All inputs come through the parameters, all outputs through the parameters or the return value.
For instance, you might be tempted to write a function that opens an HTTP connection, performs an API call, parses the result, and returns it. But you'll have a really hard time testing that function. If you decompose it into several tiny functions (one that opens a connection, one that accepts an open connection and performs the call, and one that parses the result), you'll have a much easier time testing it.
(This clicked for me when I wrote code as I've described, wrote tests for it, and later found several bugs. I realized my tests did nothing and failed to catch my bugs, because the code I'd written was impossible to test. In general, side effects and global state are the enemies of testability.)
You end up with functions that take a lot of arguments (10+), which can feel wrong at first, but it's worth it, and IDEs help enormously.
This pattern is called dependency injection.
https://en.wikipedia.org/wiki/Dependency_injection
See also, the "functional core, imperative shell" pattern.
1. don't do console I/O in leaf functions. Instead, pass a parameter that's a "sink" for output, and let the caller decide what do with it. This helps a lot when converting a command line program to a gui program. It also makes it practical to unit test the function
2. don't allocate storage in a leaf function if the result is to be returned. Try to have storage allocated and free'd in the same function. It's a lot easier to keep track of it that way. Another use of sinks, output ranges, etc.
3. separate functions that do a read-only gathering of data, from functions that mutate the data
Give these a try. I bet you'll like the results!
It sounds like too many words to refer ro plain old inversion of control and CQRS. They're both tried and true techniques.
This actually supports your broader point about always passing state to functions, and never accessing it implicitly. Although I don't know that I agree with extending that to constants, but maybe with another several decades of experience under my belt I might come to.
Zig also makes it easy for 'constants' to change based on build-specific parameters, so a different value for testing, or providing an override value in the build script. I've found that to eliminate any problems I've had in the past with global constants. Sometimes, of course, it turns out you want those values to be runtime configurable, but as refactorings go that's a relatively straightforward one.
Having an allocator implicitly passed in with a struct argument is not quite what I meant. D once had allocators as member functions, but that wound up being deprecated because the allocation strategy is only rarely tied to the struct.
A global variable is a hidden extra parameter to every function that uses it. It's much easier if the set of things you have to care about is just those in the declared parameters, not the hidden globals.
For example, types can have long names, but that doesn't work with HN. Changing a declaration to have a different type then means you've got endless cascading identifiers that need to be redone. And so on.
This is actually a good thing, every mention of that identifier is a place that you might need to adapt for the new type. Hungarian notation is an excellent coping mechanism when you have to use compilers that don't do their own type checking - which used to be a huge issue when Hungarian notation was current.
1. you become reluctant to do it
2. lots of diffs cluttering up your git history. I like my git history to be fairly narrowly targeted.
I don't use languages that don't do type checking. Microsoft uses Hungarian notation on their C interface and example code.
Would you access a global M_PI constant? Or another function name? Or would you require every dependency to passed through?
[1] i.e. a total capability based system.
The main issue comes in when you change (in the code! not as mutation!) the global immutable state and now you have to track down a bunch of usages. If it wasn't global, you could change it only in some local areas and not others.
You aren't likely to change M_PI to a new value (int 3 for performance?) so for pure constants, fine, global immutable state works. However many usages of global state are things like singletons, loggers and string messages that often eventually benefit from being passed in (i18n, testability etc.)
As to ergonomics, you can stuff all that global state into a single instance and have one more parameter that is passed around. It will still allow calls to eg change logging on their downstream functions much more easily than having singleton configuration.
But no one has time to craft such details in the code.
Specifically d+e*g I might make an exception for in a code review (and allow it), since it's such a widely known precedence in mathematics you can expect the reader and writer to know the way it goes, but any more complex and I'd reject it in the review for lack of parentheses.
I will use parens, however, for << and a couple other cases. It would be a shame to use lack of spacing to imply precedence, and yet get it wrong. Oops!
I also like to line up things to make vertical formatting of similar expressions, something a formatting program doesn't do. Hence I don't use formatters.
I would say it is a neat detail but if no one cares or uses it - it is pretty much "feel good about yourself" use and not practical one.
Oh, and you can't avoid that, say, you are working on a trading bot and that's the only "supported" way to connect to an exchange.
In the end people usually just reverse engineer and reimplement to get rid of such cursed blob. Fortunately, it works - the vendor can't effectively push all clients to update their SDK too, so all wire protocols are infinitely backward compatible.
The amount of problems this solves is incredible, and it creates none of the ops issues with configuring and launching some new kind of Docker image.
People reverse these SDK partly because it makes the codebase saner, and partly because, well, this is trading, a saner implementation is almost guaranteed to be faster than vendor's bullshit one, and guess who cares about being a little bit faster than everyone else?
You should only track source versions and build things from source as needed.
“We must minimize the need to change existing code. For adoption in existing code, decades of experience has consistently shown that most customers with large code bases cannot and will not change even 1% of their lines of code in order to satisfy strictness rules, not even for safety reasons unless regulatory requirements compel them to do so.” – Herb Sutter
with large code bases cannot and will not change even 1% of their lines of code in order to satisfy strictness rules
Do people really say this? Voice this in committee? I have been in a few companies, and one fairly large one, and all are happy to and looking forward to upgrade newer standards and already spend a lot of time updating their build systems. Changing 1% of code on top of that is probably not really that much compared“Our code is an asset” ⇒ code kept up-to-date
“Our code is a burden, but we need it” ⇒ change averse
Quite a few companies have millions and millions of lines of code. Changing 1% of it would mean changing more than 10K lines of code, perhaps even more than 100K. In much bigger code bases, where changing anything has a risk of breaking something — not just because you might make a mistake, but because your program is full of Undefined Behaviour, and changing anything might manifest latent bugs.
Given that, I'm not surprised people say that Sutter quote with a straight face.
Except, allegedly, at Google. But is there any evidence they actually do this, eg. in public code bases? Or is it just hype?
This is one of the reason why they are bad at open sourcing - their internal code almost never match what is released
But this is a multi-billion-dollar industry. If you're working on scripting a little browser "app" for a phone things may be different.
We have a large automated test suite that runs on every build and takes hours. The problem with automated tests is they only verify situations you thought of work the way you think they should, while human testers find slight variations of setup that you wouldn't think matter until they do. Human tests also find cases where the way you expect things to work don't make sense in the real world.
Changing 1% across all modules is a nightmare. Changing one module which is 1% of the code is nothing.
The first was removing `long` from the code, since a lot of code assumed its size (is it like `int` or like `long long int`?) and as machines were upgraded it caused problems.
The second was moving to C++11/14/17. Most of the difficulty was toolchains on unixen that did not support the new versions of the language, or for which support was incomplete, or for which upgrading to a version with support broke existing builds.
The third was moving to Linux from big iron unixen. As far as I understand, this initiative is still underway. It was already underway in 2011 when I joined the company.
This is a rich company with a large, healthy engineering department. I imagine that most other companies would not or could not bother.
How do I know this? I migrated a codebase of about 20m lines of C++ at a major investment bank from pre-ansi compilers to ansi conformance across 3 platforms (Linux, Solaris and Windows). Not all the code ran on all 3 platforms (I'm looking at you, Solaris) but the vast majority did. Some of it was 20 years old before I touched it - we're talking pre-STL not even just pre ansi. The team was me + one other dude for Linux and Solaris and me + one other different dude for windows, and to give you an idea the target for gcc went from gcc 2.7[1] to gcc 4[2], so a pretty massive change. The build tooling was all CMake + a bunch of special custom shell we had developed to set env vars etc and a CI/CD pipeline that was all custom (and years ahead of its time). Version control was CVS. So, single central code repo and if there was a version conflict an expert (of which I was one but it gives me cold sweats) had to go in, edit the RCS files by hand and if they screwed up all version control for everyone was totally hosed until someone restored from backup and redid the fix successfully.
While we were doing the port to make things harder there was a community of 667 developers[3] actively developing features on this codebase and it had to get pushed out come hell or high water every 2 weeks. Also, this being the securities division of a major investment bank, if anything screwed up real money would be lost.
It was a lot of work, but it got done. I did all my work using vim and quickfix lists (not any fancy pants tooling) including on windows but my windows colleague used visual C++ for his work.[4]
[1] Released in 1995
[2] Released in 2005
[3] yes. The CTO once memorably described it to me as "The number of the beast plus Kirat". Referring to one particularly prolific developer who is somewhat of a legend on Wall Street.
[4] This was in the era of "debugging the error novel" so you're talking 70 pages of ascii sometimes for a single error message with a template backtrace, and of course when you're porting you're getting tens of thousands of these errors. I actually wrote FAQs (for myself as much as anything) about when you were supposed to change "class" to "typename", when you needed "typedef typename" and when you just needed "typedef" etc. So glad I don't do that any more.
But since you say version control was CVS, then I guess it was Goldman. They still have that sheizen for SecDB/Slang today.
And I assume that "Kirat" is Kirat Singh of Goldman SecDB/JPM Athena/BofA Quartz/Beacon?
Very impressive indeed.
This invokes the imagery of a 1950s Apollo era scientist saying something serious. But I promise you there is no visionary low level language authority in the background. It’s just a staffer being influenced by the circle of blogs prominent on programming Reddit and twitter.
> no overhead principle
It’s actually nice to hear they are asserting a more conservative outlook and have some guiding design principle.
Bjarne is more of a super-bureaucrat than a designer. In the early days he pulled C++ into whatever language movements were popular. For a while it looked like Rust was having that influence.
But the outcome has been a refinement of C++ library safety features which are moderate and easy to adopt.
The other faction that has lost faith in WG21, and wants newer, safer, nimble language with powerful tooling is already heading for the exits.
Herb has even directly said that adding lifetime annotations to C++ would create "an off-ramp from C++"[1] to the other languages — and he's right, painful C++ interop is the primary thing slowing down adoption of Rust for new code in mixed codebases.
[1]: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p34...
"newer" is hopefully a non-goal.
Unfortunately, an option that is both safer and nimble doesn't appear to exist. I'm still hopeful, but at the moment it looks like rust is our future. A fate only marginally better than C++.
On the other hand, having a bit more transparency into the workgroups and their way of doing things may allow the process become a bit more efficient, approachable, and maybe would allow shedding some of the problems which have accumulated due to being so isolated from the world.
Some of the alleged events really leave a bad taste in the mouth, and really casts a shade of doubt for the future of C++.
Lastly, alienating people by shredding their work and bullying them emotionally is not the best way to build a next generation of caretakers for one of the biggest languages in the world. It might not fall overnight, but it'll certainly rot from its core if not tended properly. Nothing is too big to fail.
Instead we have greybeards and lone warriors, and million-line legacy codebases, half of which have their own idea on what a string or a thread is.
I’m not sure were you got this perception that it’s dead.
C++ remains the only game in town in many domains.
That said, _unless you work in those domains_ there is no good reason to use C++ IMHO.
Apart from the legacy codebases, there’s lots of C++ greenfield development.
” the ability to include C++ code and libraries from others ”
Libraries in vcpkg - a large number - are compatible enough to be used in this sense. It’s possible your specific domain is lacking contributions or you’ve been looking from the wrong places?
I haven't seen any perf impact of splitting stuff between files/js modules in typescript either.
What I'm guessing is that you mean that static compilers, like that of C++, need to be able to 'see' large amounts of code to make clever inlining optimizations.
Which shouldn't be the case if the code is well designed, and/or the compiler can prove invariants necessary for optimization without having to look at the body of the code.
Yeah but not because people like the language but because there is no alternative.
At work almost every one of us devs doesn't enjoy working with C++ but since many dependencies in the embedded space are written in C++ you don't have much of a saying which language you choose. For example, Qt supports basically only C++.
Rust is the only mature contender in that space currently.
Basically, it is no different than renaming .js to .ts to take advantage of some stuff in Visual Studio Code, while keep writing plain old JavaScript.
I wish I could say modules don't work, but I have yet to understand them. Which is probably a big part of its problem.
It is kind of interesting how the python community hasn't learned a thing from python 2/3. The problem isn't breaking backwards compatiblity. Probably the biggest mistake you can do is act like breaking backwards compatiblity is a big deal, therefore you should pile up as many breaking changes as possible and release them all at once so as to maximize pushback and upgrade friction.
It is in fact the exact opposite. If you break 10 libraries out of a million, you as the language developer can step in and upgrade them on behalf of the original maintainer. The users increment a library version when they increment the language version and done.
Of course we are now looking at things in hindsight and see what didn't work.
1. Forward compatibility is more important than backward compatibility. 2. Automated refactoring tools don't help with 1.
The problem wasn't that they broke a lot in Python 3. It was that you couldn't write your Python 2 in such a way as to be compatible with it until well into the transition process as the six package got popular and the devs fixed needlessly broken things in Python 2.
Every† minor point release of Perl 5 creates backward-incompatible changes. These can be opted into individually, or on a per-file basis by simply specifying the version of Perl used. It all works. Differently-versioned Perl code can call each other as much as it likes.
There was never any reason why Python 3 needed to be anything other than Python 2 with this at the top of the file:
use Python 3.n
For various values of `n`. Perhaps when enough time passes, that's just Python now, and you have to copy-paste this into all the legacy code: use Python 2
That's it. Any language can do this, they just have to decide not to make life hard for everyone.[†] Perhaps not literally every, but it may as well be.
What I mean to say is that Python as a negative example only goes so far, because an example of failure isn't a template for success. So "don't do what Python did" carries limited value for a language looking to make breaking changes. For a language looking to make a major point release, that's the future, and the future can be changed; this is what I'm interested in here.
"Do what Perl 5 did" (and do not do what Perl 6 did, up to the point it got renamed) is a great place to start, however, because it worked, works, is working. Languages are different enough that it isn't a completely transferable experience, but there's a lot to learn there.
Visual C++ and clang, alongside MSBuild and CMake/ninja.
As for ecosystem fragmentation, it has been the same old story since WG14 and WG21 exist, each compiler and platform is their own snowflake of what they actually support.
Do you have an example (of yours or others) that you could link?
I've been trying to get this up and running myself, but can't seem to whisper the right CMake prayers.
Given that the general industry approach to technical debt is "yes, more please", it is unsurprising to me that any sufficiently old C++ project still has lots and lots of plain C inside it.
That does not bode well for Microsoft. At least from the outside perspective it looks like he was the adult in the room, the driving force behind standards adoption and even trying to steer C++-the-language towards a better vision of the future.
If he is gone, MSVC will again be the unloved bastard child it has long been before Herb's efforts started to pay off. This is very disheartening news.
I'm happy he held out for this long even though he was being stonewalled every step of the way, like when Microsoft proposed std::span and it was adopted but minus the range checking (which was the whole point of std::span).
Now he has been pushing for a C++ preprocessor. Consider how desperate you have to be to even consider that as a potential solution for naysayers blocking your every move.
He has been showing it, but not pushing it. the difference is subtle but important. He is showing a lot of "what ifs" trying them, and pushing the useful ones back into the language. Reflection is on track for C++26 in large part because he inspired a lot of people with his metaclasses talk (a long time ago, but doing things right takes time)
I'm sure it's never as clean a situation as anyone would like, but hey, world is a rough place sometimes.
MSVC will continue to be used for many years, and especially the backend might see renewed effort. But I don't know about the C++ frontend specifically, I've seen complaints about more and more bugs on the cpp subreddit. It's possible MS will be investing a little less in C++.
Meanwhile on Windows side, it was made officially at Ignite that a similar decision is now to be followed upon Windows as well.
Here the official stuff, so whatever happens to MSVC is secondary,
https://azure.microsoft.com/fr-fr/blog/microsoft-azure-secur...
https://blogs.windows.com/windowsexperience/2024/11/19/windo...
This seems like one hell of an initiative for the Windows OS. That is millions of lines of C++ code, often with parts from waaay back. A friend who works on one of the OS teams told me that his team got a boomerang hire that worked on Windows back in the 90s and he was still finding parts of his code in there!
I hope this corporate interest bodes well for Rust though. It seems like for C++ it really caused a schism over the ABI break issue where Chandler et al were basically rebuffed finding some timeline to break it, and then Google dropped all their support on the committee in favor of Carbon, Rust, etc.
Likewise you will noticed MSVC is no longer riding the wave in regards to C++23, after being the first to fully support C++20.
Then there are all those other compilers out there, lost somewhere between C++14 and C++17, and most likely never moving beyond that.
Rather, it seems that as computers have gotten faster, there's been more places where safety is preferable to performance.
Two examples of stuff publicly rewritten into Rust.
Games are special that isn't what Windows security cares about in first instance, when TinyGlade is the first ever commercial success using Rust.
Yet most games are done with Unreal and Unity, and yes there is lots of C++ there, but is mostly Blueprints, Verse, C# on top, that large majority of studios reach for.
I'm aware they're trying, but I just don't believe their statements from the evidence available.
.at() is added in C++26.
Several months later, I learned I had experienced slight brain damage due to hypoxia and I’ve been slowly recovering ever since. The worst part of all of this is that I said in that post that I was enjoying golang. In other words, I had brain damage and suddenly found writing Go to be fun. Take from that what you will
OMG. ;) It's an interesting rant nonetheless. One example of this is [...] the new proposed (but not yet approved) Boost website. This is located at boost.io and I’m not going to turn that into a clickable link, and that’s because this proposed website brings with it a new logo. [...] This logo features a Nazi dog whistle. The Nazi SS lightning bolts.
The thing about dog whistles like this is that you can feign ignorance or act like someone is seeing something that isn’t there, but for something egregious it’s very hard to defend it in this case.
Of course, there’s other political dog whistles out there in the tech world right now. Justine Tunney named her C library, cosmopolitan5, which I personally believe is named after the term Rootless Cosmopolitan. This is a pejorative Soviet epithet which was used primarily during Joseph Stalin’s antisemitic campaign in the late 40s and early 50s. This is obviously much harder to prove6 as Justine has done a very good job of deleting some very eyebrow raising tweets over the years, even having them scrubbed from The Internet Archive’s Wayback Machine [...]
Justine, unfortunately, doesn’t appear to have made any amends either, at least publicly, or even acknowledged her past behavior, though she is more than happy to reference her time in the Occupy Wall Street movement. These days however, she’s busy working on llamafiles for Mozilla. For those of you not in the know, a llamafile is basically for turning an LLM’s weights into an executable.
And then he makes (yet another!) detour to AI and C++ which I am going to follow.It's a massive post though. Right now I am an hour in and probably about 75% done and I am skipping most of the linked articles. Except for the Ender's game parts. I highly recommend those.
To save people the trouble it seemed like a manic rant intended to pick several bones (at least the author is self aware enough to admit as much). It's heavy on the "trust me, I have sources" and light on actual content. It's got enough drama and insinuations from calling people liars, narcissists, to finally nazis. It veers from committee drama, to Trump, to feminism, to AI... very hard to follow.
Worthy of a daytime soap opera but other than that there's nothing notable there. Except it does make me want to avoid all these people, on both sides of whatever drama this is.
The JeanHeyd Meneide saga really says it all about what’s wrong with the C++ community. I would say their blog post is far more enlightening and avoids all the accusations and combative tone of the linked piece while still making clear what the problem is. And it’s technically enlightening to boot.
As for vcpkg - yeah, that's popular, for sure.
All these new features introduce some run-time overhead, it seems.
One of C++'s core tenants is (and has been since the 90's) zero-cost abstractions. Or really, "zero-runtime-cost abstractions"; compile times tend to increase.
Obviously some abstractions necessarily require more computation (e.g. raw pointers vs reference-counted smart pointers). But in many cases new features (if implemented correctly!) give better semantics and additional compile-time safety while still compiling down to equivalent binary.
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p34...
but the current ABI _forces_ some abstractions to have unnecessary cost. For example:
"Why can a T* be passed in register, but a unique_ptr<T> cannot?" https://stackoverflow.com/q/58339165/1593077
another example is improvements in the implementation of parts of the standard library.
And that is not the only thing that prevents zero-cost abstraction. C++ does not support pointer restrction, see:
https://stackoverflow.com/tags/restrict-qualifier/info
in practice, compilers support it for some contexts.
(Anoter, minor issue is the discrepancy of "No viral annotation" and "no heavy annotation" with the need to mark things noexcept to avoid exceptio handling overhead.)
For restrict: Universally supported as `__restrict`, thus not a priority for anyone to "officially" solve. Most major performance complaints fall into this category. Eg, std::regex is bad, sure, but nobody uses std::regex so fixing it doesn't matter.
SysV/Itanium/Win64 knows nothing about the abstract difference between a 64-bit value and a 64-bit value inside a class instance. Don't see what prevents the solution from the language side.
> Universally supported as `__restrict`
1. That's not C++. If we're talking about what compilers can offer outside the language standard - that's a different discussion. We don't have to standardize, then, just get a working implementation somewhere and lobby other compiler-makers to adopt it. A compiler might implement Baxter's "safe mode" idea as a non-standard extension, for example.
2. Even the compilers supoorting `__restrict` only support it for parameters of functions - nowhere else.
The standard has nothing to say about calling convention. Calling conventions are defined by the ABI standards. unique_ptr is a class template, how a class is passed between routines is defined by ABI standards, ergo how unique_ptr is passed between routines is defined by the ABI standards. I don't know what else you're implying here. Unless you're saying we should have language-level smart pointers, not class templates, in which case yes that's an awful idea.
> That's not C++...
If you're arguing about some abstract, Platonic ideal of C++ that is divorced from the actual implementations that exist in the world, I don't know what your point is. We write code to be compiled by compilers that exist, not printed out and contemplated in a museum. The compilers support __restrict, people use it all the time, so its not a problem.
> Even the compilers...
Where else do you want it? What would its meaning even be to something like a member variable?
Restrict is pointless in any scenario that doesn't involve a potentially aliasing ABI boundary, ie, function parameters. In every other scenario it is ignored.
The ABI defines the calling convention but it is restricted by the guarantees the language already makes. Specifically this part from the System V ABI spec quoted in the linked SO discussions:
> If a C++ object has either a non-trivial copy constructor or a non-trivial destructor, it is passed by invisible reference (the object is replaced in the parameter list by a pointer that has class INTEGER).
> An object with either a non-trivial copy constructor or a non-trivial destructor cannot be passed by value because such objects must have well defined addresses. Similar issues apply when returning an object from a function.
What is needed so that we can have smart pointers passed in registers is:
a) A way to specify that the address of the object itself may change (even though the object is not trivially destructible). P1144's [[trivially_relocatable]] would cover this requirement.
b) The System V ABI needs to be adapted to make use of this new information. Note that this also requires to make the callee responsible for destruction which may or may not be desirable generally (makes fusing allocation and deallocation harder or impossible in many cases) - raw pointers leave this decision to the programmer.
c) The standard library needs to add the new attribute to unique_ptr et al. This would now be an ABI break (the goal is to improve the ABI, duh).
So in essence BOTH the language AND the ABI need to be adapted to achieve the optimum behavior here. And even then with only [[trivially_relocatable]] there is still a difference with raw pointers less flexible in which function the destruction happens. And making the ABI depend on [[trivially_relocatable]] limits where the attribute can be added - so the the ABI depends on the the language spec and the standard library spec depends on the ABI for its compatibility requirements.
If the ABI refers to the standard, the standard can constrain/manipulate the ABI. Specifically, the standard could say destructors and copy ctors must, under certain conditions, be considered trivial for ABI purposes.
But really, account42 has it right when he talks about co-changes to the language and the ABI - that's the better way to think about it.
> I don't know what else you're implying here.
You're feigning ignorance. I said what I implied.
> Where else do you want it?
In struct fields of course. (Also the corner case of restriction of the "this" pointer, but that could arguably be covered by function parameters + fields)
Uh, they took decades to implement a bunch of C99 features. Is that predictive? I suspect it is.
This links to:
https://thephd.dev/finally-embed-in-c23
It was a fascinating story, particularly about how people finally coming to terms with accepting that a seemly ugly way of doing things really is the best way (you just can't "parse better").
The feature itself is interesting too.
#embed has to pretend - in principle - that we're going to conjure all these byte values into existence, as actual numbers, and then by the "as if" rule the compiler is not really going to do that because it would be crazy slow. The reality that we're just going to shove the data into the program as if it was an array is an (obviously, implemented everywhere) optimisation, rather than part of the language specification.
The analogous Rust `include_bytes!` just gets you a &'static [u8; N] -- an immutable reference to an array of N bytes which lives forever.
At first I thought OK, well, maybe the C approach lets you do some clever compile stuff that Rust can't do. Nope. If I have a compile time function checksum which calculates a 64-bit checksum of the slice passed by immutable reference - and a file of 128MB of data called firmware.bin, Rust is completely fine with let sum = checksum(include_bytes!("firmware.bin")); and that results in a 64-bit value, the 128MB file evaporated after being checksummed.
I also think there's a place where it can easily be supplanted, but currently cross platform native software has Qt and bindings for it in other languages are mixed.
In performance critical things, Rust still doesn't feel like the final answer since you end up cloning a lot and refactors are very painful. Go obviously has it's issues since SIMD support is non-existent and there is limited control over garbage collection, though it works well for web APIs.
.NET does however offer best-in-class portable SIMD API and large API surface of platform intrinsics both of which are heavily used by CoreLib and many performance-oriented libraries. You can usually port intrinsified implementations hand-written in C++ to C# while making the code more readable and portable and not losing any performance (though sometimes you have to make different choices to make the compiler happy).
https://github.com/dotnet/runtime/blob/main/docs/coding-guid...
Autovectorization is usually very fragile and in areas where you care about it hand-written implementation always provides much better results that will not randomly break on minor changes to compiler version or the code, that must be carefully guarded against.
It would be still nice to have it eventually, and I was told that JIT team actively discusses this but there are just many more lower hanging fruits that will light up in disproportionately more instances of user code.
If it's any consolation, Clang/LLVM is not a silver bullet either and you will find situations where .NET's compiler output is competitive or even better: https://godbolt.org/z/3aKnePaez
> * Relatively modern, capable tech corporations that understand that their code is an asset. (This isn’t strictly big tech. Any sane greenfield C++ startup will also fall into this category.)
> * Everyone else. Every ancient corporation where people are still fighting over how to indent their code, and some young engineer is begging management to allow him to set up a linter.
Well said.
And because of this, a lot of the first is leaving for greener pastures.
However, I think the author is a little off on the root cause. They emphasize tooling: the ability to build reliably and cleanly from source. That's a piece of it, but a relatively small piece.
I think the real distinguishing factor between the two camps is automated testing. The author mentions testing a couple of times, but I want to emphasize how critical that is.
If you don't have a comprehensive set of test suites that you are willing to rely on when making code changes, then your source code is a black box. It doesn't matter if you have the world's greatest automated refactoring tools that output the most beautiful looking code changes. If you don't have automated tests to validate that the change doesn't break an app and cost the company money, you won't be able to land it.
Working on a "legacy C++ app" (like, for example, Madden NFL back when I was at EA) was like working on a giant black box. You could fairly confidently add new features and new code onto the side. But if you wanted to touch existing code, you needed a very compelling reason to do so in order to outweigh the risk of breaking something unexpectedly. Without automated tests, there was simply no reliable way to determine if a change caused a regression.
And, because C++ is C++, even entirely harmless seeming code changes can cause regressions. Once you've got things like reinterpret_cast<>, damn near any change can break damn near anything else.
So people working in these codebases behave sort of like surgeons with a "do no harm" philosophy. They touch as little as possible, as non-invasively as possible. Otherwise, the risk of harming the patient is too high.
It's a miserable way to program long-term. But it's really hard to get out of that mess once you're in it. It takes a monumental amount of political capital from engineering leadership to build a strong testing culture, re-architect a codebase to be testable, and write all the tests.
A lot of C++ committee changes aimed at legacy C++ developers are about "how can we help these people that are already in a mess survive?" That's a very different problem than asking, "Given a healthy, tested codebase, how can we make developers working in it go faster?"
Having also worked at a few gamedev studios, IME there isn't a real distinction between the two since it is always a matter of time for the former to become the latter.
Sometimes it doesn't even take that long, all it takes is a single innocuous vertical slice with a pointlessly immovable deadline to inject enough harm in a codebase so you spend the next year fighting bugs that shouldn't have existed in the first place while also having to do everything else at the same time (and all planned timeframes made with only the "everything else" in mind, of course).
IMO even if it doesn't sound good, it is much more practical to learn how to deal with the mud than assume pigs do not exist :-P
That was very much my experience at EA, but has definitely not been my experience at Google. While everyone struggles with tech debt, at Google I've worked in many codebases that have been continuously well-maintained with good test coverage for over a decade.
Really, once you build a culture that says, "People not on your team may edit your code without asking and will rely on your tests to make sure they don't break things,", teams get highly incentivized to write tests.
I think that tests are a sure-fire way to improve the quality of your code, but I'd throw another piece in to the ring: sanitizers [0]. Projects that have good tests and run them regularly with TSan/ASan/UBSan in my experience are much better to work on because it means that it's much less likely there's deep seeded issues that are lurking. It gives you increased confidence that you're not introducing hard-to-detect issues as you go.
These tools aren't just exclusive to C++. I've said the same thing about C, Go, Zig, Odin, etc. Projects that use them (and have good automated tests) tend to be in good shape, and projects that don't tend to take a long time to make any progress on.
In hindsight I would've probably written a few things differently, but I really didn't want to fall into a trap of getting stuck editing.
And I do take asbestos as a serious example. Asbestos is still manufactured and used! Believe or not there are "safe" uses of asbestos and there are protocols around using them. Nevermind the fact that there is a lot of FUD and dishonesty about where exactly the line cuts on what is safe versus not safe...for example we are finding out how brake dust affects the wider environment as we crawl out from under the tent of utter misinformation of a highly motivated entrenched industry.
I feel like this is not a new human phenomenon. We made particularly poor choices in what tech we became dependent on, and lo and behold, the entrenched interests keep telling us it's not that bad and we should keep doing it because...reasons.
It will eventually play out the way it must; C++ might seem a lot more innocuous than asbestos, and in some ways that's true, but it resists all effort to reform it and will probably end up needing to just be phased out.
I feel it would be best for the C++ language that its development would stop. There's no way to fix its current problems. The fact that it stayed compatible with previous iterations over so many years is an accomplishment, almost a miracle, it should be cherished. Deviating from that direction doesn't make sense. Keeping that does not make sense either.
Programming languages benefit far more from a robust implementation, tooling, and good technical documentation (which may read like a standard) than from a prescriptive standard. The latter generates enormous waste, for what?
ABI stability in the context of the standards committee is about library ABI, specifically the standard library. When the committee updated the wording about C++'s std::string in C++11, it meant implementers needed to change the layout of a std::string, making this "new" std::string incompatible with the "old" std::string. Any libraries passing std::string across API boundaries needed to be recompiled with the "new" std::string.
This has no effect on FFIs for interop with other languages, which are not passing STL types across language boundaries to begin with (a std::string has no meaning in Python).
ABI stability for the standard library is motivated by large, old, coroporate codebases which had poor API practices, passed STL types across ABI boundaries, and subsequently lost access to the source code of those libraries and applications or otherwise cannot recompile them for some reason. Many people question the wisdom of catering to such users.
It's also motivated by Linux distributions and other complex systems where rebuilding and installing the world in one go is not possible/feasible.
I strongly disagree with this. The more code you have, the more resources you have to spend maintaining it. There is a very relevant example close by in the post: the bit about Google having a clang-based tool that can refqactor the entire codebase. Great! Problem is, an engineer had to spend their time writing that, and you had to pay that engineer money - all because you have an unmanageable amount of code.
The real tech asset is processes: the things you have figured out in order to manage such an ungodly amount of code. Your most senior engineers, specifically what's in their heads, are an asset too.
There must be some extremely ideological reason behind these horrible “modern” C++ standards.
There are some good trend happening during C++ 11, but now it is completely out of control now.
Were namespace versions determined to not solve this problem? That would be the most ironic thing after all if the change management system introduced in c++11 to avoid std::string is either unused, untrusted, or unworkable for the purpose it was intended.
if your library has a public function that takes a string argument or returns a string or a struct containing a string then that implicitly is an ABI dependency on either std::v1 or std::v2. The library would have to actively add versions for both ABIs. And if the stdlib type is used in a struct/class it can't even differentiate between those types in the ABI.
It would be up to the library to decide what to do. It could decide to only expose v1 or just v2.
> And if the stdlib type is used in a struct/class it can't even differentiate between those types in the ABI.
This could be solvable through annotations in the header (explicitly written or implicit through the use of flags) that indicate the version of the types used the std types are referenced implicitly (you could even have warnings and errors to that effect if you encounter it).
It seems like a solvable problem to me.
Being really good at C++ almost demands that you surrender entire lobes of your brain to mastering the language. It is too demanding and too dehumanizing. Developers need a language and a complete tool chain that is designed as a cohesive whole, with as little implicit behavior, special cases and clever tricks as possible. Simple and straight-forward. Performance tweaks, memory optimizations and anything else that is not straightforward should be done exclusively by the compiler. I.E. we should be leveraging computers to do what they do best, freeing our attention so we can focus on the next nifty feature we're adding.
Zig is trying to do much of this, and it is a huge undertaking. I think an even bigger undertaking than what Zig is attempting is needed. The new "language" would also include a sophisticated IDE/compiler/static-analyzer/AI-advisor/Unit-Test-Generator that could detect and block the vast majority of memory safety errors, data races and other difficult bugs, and reveal such issues as the code is being written. The tool chain would be sophisticated enough to handle the cognitive load rather than force the developer to bear that burden.
My Rational for Using C++ in 2024: (A) Extreme computational performance desired. (B) I learned C++ 20 years back. (C) C++ has good enough Cross-Platform (OS) compatibility.
A good programming platform has to consist of tooling which includes package managers, compilers, linters, etc. Ideally, in this orbit you would also have "Language Servers" or similar. At the very least, the compiler and language should be written with this in mind, e.g. written for the ground up to support incremental compilation and so on.
Go, C#, and Rust all have tooling-first and more importantly first-party tooling. The people who design the language MUST be able to iterate quickly with the people who make the compiler, who in turn should be able to walk down the hall and talk to the people who make the package manager, the package manager repository, and so on.
I'll will not, thanks.