7 ms·
The best thing the C++ WG could do is to spend an entire release cycle working on modules and packaging. It's nice to have new features, but what is really kil
by zarzavat 7mo ago
The best thing the C++ WG could do is to spend an entire release cycle working on modules and packaging.
It's nice to have new features, but what is really killing C++ is Cargo. I don't think a new generation of developers are going to be inspired to learn a language where you can't simply `cargo add` whatever you need and instead have to go through hell to use a dependency.
- luka598 7mo agoAgreed, arcane cmake configs and or bash build scripts are genuinely off-putting. Also cpp "equivalents" of cargo which afaik are conan and vcpkg are not default and required much more configuring in comparison with cargo. Atleast this was my experience few years ago.
- mgaunard 7mo agoIt's fundamentally different; Rust entirely rejects the notion of a stable ABI, and simply builds everything from source. C and C++ are usually stuck in that antiquated thinking that you should build a module, package it into some libraries, install/export the library binaries and associated assets, then import those in other projects. That makes everything slow, inefficient, and widely dangerous. There are of course good ways of building C++, but those are the exception rather than the standard.
- stackghost 7mo ago>There are of course good ways of building C++, but those are the exception rather than the standard. What are the good ways?
- lstodd 7mo ago"Do not do it" looks like the winning one nowadays.
- mgaunard 7mo agoBuild everything from source within a single unified workspace, cache whatever artifacts were already built with content-addressable storage so that you don't need to build them again. You should also avoid libraries, as they reduce granularity and needlessly complexify the logic. I'd also argue you shouldn't have any kind of declaration of dependencies and simply deduce them transparently based on what the code includes, with some logic to map header to implementation files.
- stackghost 7mo ago>Build everything from source within a single unified workspace, cache whatever artifacts were already built with content-addressable storage so that you don't need to build them again. Which tool do you use for content-addressable storage in your builds? >You should also avoid libraries, as they reduce granularity and needlessly complexify the logic. This isn't always feasible though. What's the best practice when one cannot avoid a library?
- mgaunard 7mo agoYou can use S3 or equivalent; a normal filesystem (networked or not) also works well. You hash all the inputs that go into building foo.cpp, and then that gives you /objs/<hash>.o. If it exists, you use it; if not, you build it first. Then if any other .cpp file ever includes foo.hpp (directly or indirectly), you mark that it needs to link /objs/<hash>.o. You expand the link requirements transitively, and you have a build system. 200 lines of code. Your code is self-describing and you never need to write any build logic again, and your build system is reliable, strictly builds only what it needs while sharing artifacts across the team, and never leads to ODR.
- stackghost 7mo agoInteresting, thanks.
- maccard 7mo agoThe problem is doing this requires a team to support it that is realistically as large as your average product team. I know Bazel is the solution here but as someone who has used C++, modified build systems and maintained CI for teams for years, I have never gotten it to work for anything more than a toy project.
- NetMageSCW 7mo agoI would suggest importing binaries and metadata is going to be faster than compiling all the source for that.
- mgaunard 7mo agoYou'd be wrong. If the build system has full knowledge on how to build the whole thing, it can do a much better job. Caching the outputs of the build is trivial. If you import some ready made binaries, you have no way to guarantee they are compatible with the rest of your build or contain the features you need. If anything needs updating and you actually bother to do it for correctness (most would just hope it's compatible) your only option is usually to rebuild the whole thing, even if your usage only needed one file.
- tialaramex 7mo agoIt's not true that Rust rejects "the notion of a stable ABI". Rust rejects the C++ solution of freeze everything and hope because it's a disaster, it's less stable than some customers hoped and yet it's frozen in practice so it disappoints others. Rust says an ABI should be a promise by a developer, the way its existing C ABI is, that you can explicitly make or not make. Rust is interested in having a properly thought out ABI that's nicer than the C ABI which it supports today. It'd be nice to have say, ABI for slices for example. But "freeze everything and hope" isn't that, it means every user of your language into the unforeseeable future has to pay for every mistake made by the language designers, and that's already a sizeable price for C++ to pay, "ABI: Now or never" spells some of that out and we don't want to join them.
- zozbot234 7mo ago> It'd be nice to have say, ABI for slices for example. The de-facto ABI for slices involves passing/storing pointer and length separately and rebuilding the slice locally. It's hard to do better than that other than by somehow standardizing a "slice" binary representation across C and C-like languages. And then you'll still have to deal with existing legacy code that doesn't agree with that strict representation.
- saagarjha 7mo agoIf Rust makes no progress towards choosing an ABI and decides that freezing things is bad, then Rust is de facto rejecting the notion of a stable ABI.
- tialaramex 7mo agoRust is just a bit less than 11 years old, C++ was 13 years old when screwed up std::string ABI, so, I think Rust has a few years yet to do less badly. Obviously it's easier to provide a stable ABI for say &'static [T] (a reference which lives forever to an immutable slice of T) or Option<NonZeroU32> (either a positive 32-bit unsigned integer, or nothing) than for String (amortized growable UTF-8 text) or File (an open file somewhere on the filesystem, whatever that means) and it will never be practical to provide some sort of "stable ABI" for arbitrary things like IntoIterator -- but that's exactly why the C++ choice was a bad idea. In practice of course the internal guts of things in C++ are not frozen, that would be a nightmare for maintenance teams - but in theory there should be no observable effect from such changes and so that discrepancy leads to endless bugs where a user found some obscure way to depend on what you'd hidden inside some implementation detail, the letter of the ISO document says your change is fine but the practice of C++ development says it is a breaking change - and the resulting engineering overhead at C++ vendors is made even worse by all the UB in real C++ software. This is the real reason libc++ still shipped Quicksort as its unstable sort when Biden was President, many years after this was in theory prohibited by the ISO standard† Fixing the sort breaks people's code and they'd rather it was technically faulty and practically slower than have their crap code stop working. † Tony's Quicksort algorithm on its own is worse than O(n log n) for some inputs, you should use an introspective comparison sort aka introsort here, those existed almost 30 years ago but C++ only began to require them in 2011.
- zrm 7mo ago> C and C++ are usually stuck in that antiquated thinking that you should build a module, package it into some libraries, install/export the library binaries and associated assets, then import those in other projects. That makes everything slow, inefficient, and widely dangerous. It seems to me the "convenient" options are the dangerous ones. The traditional method is for third party code to have a stable API. Newer versions add functions or fix bugs but existing functions continue to work as before. API mistakes get deprecated and alternatives offered but newly-deprecated functions remain available for 10+ years. With the result that you can link all applications against any sufficiently recent version of the library, e.g. the latest stable release, which can then be installed via the system package manager and have a manageable maintenance burden because only one version needs to be maintained. Language package managers have a tendency to facilitate breaking changes. You "don't have to worry" about removing functions without deprecating them because anyone can just pull in the older version of the code. Except the older version is no longer maintained. Then you're using a version of the code from a few years ago because you didn't need any of the newer features and it hadn't had any problems, until it picks up a CVE. Suddenly you have vulnerable code running in production but fixing it isn't just a matter of "apt upgrade" because no one else is going to patch the version only you were using, and the current version has several breaking changes so you can't switch to it until you integrate them into your code.
- mgaunard 7mo agoThis is all wishful thinking disconnected from practicalities. First you confuse API and ABI. Second there is no practical difference between first and third-party for any sufficiently complex project. Third you cannot have multiple versions of the same thing in the same program without very careful isolation and engineering. It's a bad idea and a recipe for ODR violations. In any non-trivial project there will be complex dependency webs across different files and subprojects, and humans are notoriously bad at packaging pieces of code into sensible modules, libraries or packages, with well-defined and maintained boundaries. Being able to maintain ABI compatibility, deprecating things while introducing replacement etc. is a massive engineering work and simply makes people much less likely to change the way things are done, even if they are broken or not ideal. That's an effort you'll do for a kernel (and only on specific boundaries) but not for the average program.
- jjmarr 7mo ago"Stable ABI" is a joke in C++ because you can't keep ABI and change the implementation of a templated function, which blocks improvements to the standard library. In C, ABI = API because the declaration of a function contains the name and arguments, which is all the info needed to use it. You can swap out the definition without affecting callers. That's why Rust allows a stable C-style ABI; the definition of a function declared in C doesn't have to be in C! But in a C++-style templated function, the caller needs access to the definition to do template substitution. If you change the definition, you need to recompile calling code i.e. ABI breakage. If you don't recompile calling code and link with other libraries that are using the new definition, you'll violate the one-definition rule (ODR). This is bad because duplicate template functions are pruned at link-time for size reasons. So it's a mystery as to what definition you'll get. Your code will break in mysterious ways. This means the C++ committee can never change the implementation of a standardized templated class or function. The only time they did was a minor optimization to std::string in 2011 and it was such a catastrophe they never did it again. That is why Rust will not support stable ABIs for any of its features relying on generic types. It is impossible to keep the ABI stable and optimize an implementation.
- uecker 7mo ago"That makes everything slow, inefficient, and widely dangerous." There nothing faster and more efficient than building C programs. I also not sure what is dangerous in having libraries. C++ is quite different though.
- hansvm 7mo agoOf course there is. Raw machine code is the gold standard, and everything else is an attempt to achieve _something_ at the cost of performance, C included, and that's even when considering whole-program optimization and ignoring the overhead introduced by libraries. Other languages with better semantics frequently outperform C (slightly) because the compiler is able to assume more things about the data and instructions being manipulated, generating tighter optimizations.
- uecker 7mo agoI was talking about building code not run-time. But regarding run-time, no other language does outperform C in practice, although your argument about "better semantics" has some grain of truth in it, it does not apply to any existing language I know of - at least not to Rust which is in practice for the most part still slower than C.
- mgaunard 7mo agoODR violations are very easy to trigger unless you build the whole thing from source, and are ill-formed, no diagnostic required (worse than UB).
- groundzeros2015 7mo agoI didn’t think header only was that bad - now we have a nightmare of incompatible standards and compilers.
- mgaunard 7mo agoIn my experience, no one does build systems right; Cargo included. The standard was initially meant to standardize existing practice. There is no good existing practice. Very large institutions depending heavily on C++ systematically fail to manage the build properly despite large amounts of third party licenses and dedicated build teams. With AI, how you build and integrate together fragmented code bases is even more important, but someone has yet to design a real industry-wide solution.
- lenkite 7mo agoSpeedy convenience beats absolute correctness anyday. Humans are not immortal and have finite amount of time for life and work. If convenience didn't matter, we would all still be coding in assembly or toggling hardware switches.
- jjmarr 7mo agoC++ builds are extremely slow because they are not correct. I'm doing a migration of a large codebase from local builds to remote execution and I constantly have bugs with mystery shared library dependencies implicitly pulled from the environment. This is extremely tricky because if you run an executable without its shared library, you get "file not found" with no explanation. Even AI doesn't understand this error.
- mgaunard 7mo agoThe dynamic linker can clearly tell you where it looks for files and in which order, and where it finds them if it does. You can also very easily harden this if you somehow don't want to capture libraries from outside certain paths. You can even build the compiler in such a way that every binary it produces has a built-in RPATH if you want to force certain locations.
- jjmarr 7mo agoThat is what I'm doing so I can get distributed builds working. It sucks and has taken me days of work.
- ho_schi 7mo agoI’m still surprised how people ignore Meson. Please test it :) https://mesonbuild.com/ https://mesonbuild.com/ And Mesons awesome dependency handling: https://mesonbuild.com/Dependencies.html https://mesonbuild.com/Dependencies.html https://mesonbuild.com/Using-the-WrapDB.html#using-the-wrapdb https://mesonbuild.com/Using-the-WrapDB.html#using-the-wrapd... https://nibblestew.blogspot.com/2026/02/c-and-c-dependencies-dont-dream-it-be-it.html https://nibblestew.blogspot.com/2026/02/c-and-c-dependencies... I suffered with Java from Any, Maven and Gradle (the oldest is the the best). After reading about GNU Autotools I was wondering why the C/C++ folks still suffer? Right at that time Meson appeared and I skipped the suffering. * No XML * Simple to read and understand * Simple to manage dependencies * Simple to use options Feel free to extend WrapDB.
- einpoklum 7mo agoBuild system generators (like Meson, autotools, CMake or any other one) can't solve programming language module and packaging problems, even in principle. So, it's not clear what your argument is here. > I’m still surprised how people ignore Meson. Please test it :) I did just that a few years ago and found it rather inconvenient and inflexible, so I went back to ignoring it. But YMMV I suppose. > After reading about GNU Autotools Consider Kitware's CMake.
- light_hue_1 7mo agoMeson merges the crappy state of C/C++ tooling with something like Cargo in the worst way possible: by forcing you to handle the complexity of both. Nothing about Meson is simple, unless you're using it in Rust, in which case you're better off with Cargo. In C++ you don't get lockfiles, you don't get automatic dependency install, you don't get local dependencies, there's no package registry, no version support, no dependency-wide feature flags (this is an incoherent mess in Meson), no notion of workspaces, etc. Compared to Cargo, Meson isn't even in the same galaxy. And even compared to CMake, Meson is yet another incompatible incremental "improvement" that offers basically nothing other than cute syntax (which in an era when AI writes all of your build system anyway, doesn't even matter). I'd much rather just pick CMake and move on.
- PerryStyle 7mo ago
- saghm 7mo agoTo me, the most important feature of Cargo isn't even the dependency management but that I don't ever need to tell it which files to compile or where to find them. The fact that it knows to look for lib.rs or main.rs in src and then recursively find all my other modules without me needing to specify targets or anything like that is a killer feature on its own IMO. Over the past couple of years I've tried to clone and build a number of dotnet packages for various things, but for an ecosystem that's supposedly cross-platform, almost none of them seem to just work by default when I run `dotnet build` and instead require at least some fixes in the various project files. I don't think I've ever had an issue with a Rust project, and it's hard not to feel like a big part of that is because there's not really much configuration to be done. The list of dependencies is just about the only thing in there that effects the default build; if there's any other configuration other than that and the basic metadata like the name, the repo link, the license, etc., it almost always will end up being specifically for alternate builds (like extra options for release builds, alternate features that can be compiled in, etc.).
- vlovich123 7mo agoFor me the lack of dependency hell until I hit a c/c++ component somewhere in the build is the real winner.
- kibwen 7mo ago> The fact that it knows to look for lib.rs or main.rs in src and then recursively find all my other modules without me needing to specify targets or anything like that is a killer feature on its own IMO. In the interest of pedantry, locating source files relative to the crate root is a language-level Rust feature, not something specific to Cargo. You can pass any single Rust source file directly to rustc (bypassing Cargo altogether) and it will treat it as a crate root and locate additional files as needed based on the normal lookup rules.
- saghm 7mo agoInteresting, I didn't realize this! I know that a "crate" is specifically the unit of compilation for rustc, but I assumed there was some magic in cargo that glued the modules together into a single AST rather than it being in rustc itself. That being said, I'd argue that the fact that this happens so transparently that people don't really need to know this to use Cargo correctly is somewhat the point I was making. Compared to something like cmake, the amount of effort to use it is at least an order of magnitude lower.
- daemin 7mo agoI may be in the minority but I like that C++ has multiple package managers, as you can use whichever one best fits your use case, or none at all if your code is simple enough. It's the same with compilers, there's not one single implementation which is the compiler, and the ecosystem of compilers makes things more interesting.
- lmm 7mo agoMultiple package managers is fine, what's needed is a common repository standard (or even any repository functionality at all). Look at how it works in Java land, where if you don't want to use Maven you can use Gradle or Bazel or what have you, or if you hate yourself you can use Ant+Ivy, but all of them share the same concept of what a dependency is and can use the same repositories.
- zarzavat 7mo agoAlso, having one standard packaging format and registry doesn't preclude having alternatives for special use cases. There should be a happy path for the majority of C++ use cases so that I can make a package, publish it and consume other people's packages. Anyone who wants to leave that happy path can do so freely at their own risk. The important thing is to get one system blessed as The C++ Package Format by the standard to avoid xkcd 927 issues.
- daemin 7mo agoThat would actually be pretty cool. Though I think there might have been papers written on this a few years ago. Does anyone know of these or have any updates about them?
- nickelpro 7mo agoCPS[1] is where all the effort is currently going for a C++ packaging standard, CMake shipped it in 4.3 and Meson is working on it. Pkgconf maintainer said they have vague plans to support at some point. There's no current effort to standardize what a package registry is or how build frontends and backends communicate (a la PEP 517/518), though its a constant topic of discussion. [1]: https://github.com/cps-org/cps https://github.com/cps-org/cps
- pjmlp 7mo agoIt is already there, with vcpkg and conan, alongside cmake. You cannot cargo add Unreal, LLVM, GCC, CUDA,...
- superxpro12 7mo ago100% agree this is something that would have immediate, high value impact. The fact that building C++ is this opaque process defined in 15 different ways via make, autoconf, automake, cmake, ninja, with 50 other toolchains is something that continues to create a barrier to entry. I still remember the horrors of trying to compile c++ in 2004 on windows without anything besides borland... Standardizing the build system and toolchain needs to happen. It's a hard problem that needs to be solved.
- maccard 7mo ago> Standardizing the build system and toolchain needs to happen. It's a hard problem that needs to be solved. I agree, and I also think it’s never happening. It requires agreeing on so many things that are subjective and likely change behaviour. C++ couldn’t even manage to get module names to be required to match the file name. That was for a new feature that would have allowed us to figure out esports without actually opening the file…