7 ms·
C++ Modules: Packaging Story
- deutschepost 3y agoI think Conan and Modules will make c++ more popular in the future. It is a behemoth of a language. Very hard to master and it is still too easy to produce difficult to debug errors. So it will be very important, that the language changes to address these. But in working with c++ day to day I feel the most annoying things are indeed dealing with dependencies and headers. It can be a pain to set up a complex c++ project on a new machine. Even with Conan it can still be a pain because configuration management is a mess in c++. As for headers I can’t wait to see them rot in hell. Some people say that they can help you reason about your Program, but this is a very small positive point. Too many times I had problems with includes which only worked because a Translation Unit included some other header beforehand. I once read a blogpost of the developer of SumatraPDF where they describe that they never include anything directly[1]. This can be an improvement for compile time. But if someone else is working on your code or has to refactor it, it can be impossible to read yourself into it or track any compile time error. If one would only use modules these errors would simply not exist. [1]https://blog.kowalczyk.info/article/96a4706ec8e44bc4b0bafda2d9ba502f/extreme-include-discipline-for-c-code.html https://blog.kowalczyk.info/article/96a4706ec8e44bc4b0bafda2...
- jb1991 3y agoWhen you consider that most popular modern languages long ago abandoned the idea of a separate header file, you see that the advantages far outweigh the disadvantages.
- layer8 3y agoThe problem isn't separate header files, it's textual inclusion. With C++ modules you still have separate module interface units. Separation between interface and implementation is important for enabling circular dependencies (mutual use) between implementations.
- otabdeveloper4 3y agoExcept for C, no popular language (modern or ancient) has ever used header files. So it's not a story of "abandoning", more like an insane C idiosyncrasy that wasn't ever used anywhere else.
- stabbles 3y agoIn Python I sometimes miss "header files". Quite often you see: import x class Foo: def bar(self): import y # break circular import y.something() Would be nice to be able to import `Foo` without pulling in also `y`, or moving `y` inline. Can be solved in different ways, but you see inline imports everywhere.
- colejohnson66 3y agoThat's not a problem with modules, per se, but a problem with Python's module system. Importing a module in Python just executes the whole file. JavaScript used to have this issue with `require` years ago. Each `require` statement essentially executed that import right then and there, and would bug out if circular dependencies existed. However, they fixed it with `import`. Almost every other module-based language does not have issues with circular dependencies. Python, in theory, could follow their lead, but they won't. EDIT: I wasn't as clear as I could be. The issue again isn't modules, but scripting languages that allow top-level statements at all. Intermixing types and method declarations with executable code makes circular decencies an issue. Compiled languages don't allow top-level statements, so they don't have the dependency resolution problem.
- paulddraper 3y ago> Each `require` statement essentially executed that import right then and there That still happens. It takes a lot of magic trickery to make cyclical require/imports work for JavaScript and a lot of times they don't/can't.
- colejohnson66 3y ago
- charcircuit 3y ago>If one would only use modules these errors would simply not exist. In exchange for reducing parallelism because you are not using forward declerations to break dependency chains up.
- silon42 3y ago+1 I once refactored part of my code for headers to only #include forward declarations (and few generics, of course), except where really needed (implementation) It made compile times 2x faster, probably could get 3x if fully done.
- gpderetta 3y agoIt is going to be a struggle. Headers work well for full recompilation as they provide significant parallelism. But are very bad for incremental compilation.
- pjmlp 3y agoOnly for the mindset stuck in the classical UNIX tooling approach, module based languages have used parallel compilation for a long time. The big difference is that those communities embrace compiler and tooling are part of the same story. Thankfully now we have a tools working group trying to bring C++ community into the modern world of module based compiler toolchains.
- charcircuit 3y agoShow me a module based language that maintains the same parallelism of compiles.
- pjmlp 3y agoC#, compiling in parallel since the C++ compiler was replaced with a C# written one, beating C++ traditional compile times hands down. Go, compiling code in parallel since Go 1.19. One is able to compile the whole toolchain from scratch faster than many C++ codebases. Active Oberon, compiling in parallel via the Paco toolchain since 2003, a full graphical workstation OS, built in a couple of minutes. Ada, parallel compilation available in most toolchains since 1989. Also available in GNAT. Delphi, yet another example. It is C++ that needs to get up to date with modern toolchains and away from hacks like unity builds.
- gpu64 3y agoIt's crazy to see how much work header files make the C++ compiler do just to build hello world: $ gcc -E hello.c | cloc --force-lang=c - 419 $ g++ -E -std=c++11 hello_iostream.cpp | cloc --force-lang=c++ - 20707 $ g++ -E -std=c++20 hello_iostream.cpp | cloc --force-lang=c++ - 30876 $ g++ -E -std=c++20 hello_std_format.cpp | cloc --force-lang=c++ - 42757 Just bumping the C++ std version can add thousands of lines of code.
- nly 3y agoiostreams are more or less just a straight conversion of everything that C streams were (but nobody ever used) in to polymorphic interfaces and templates. It's a shame they add so much header bloat, but there's a lot of functionality in there and it's hard to see a way it could have been avoided if you want type safety.
- leni536 3y agoMost of the sin of iostream is having a humonguous overload set for operator<<. AFAIK it's not too bad to just compile the stream headers, but the overload resolution for `std::cout << anything` is expensive.
- phaedrus 3y agoAnd after all that effort, an operator overload is still the "wrong" API for the job anyway, because: it's missing a crucial argument! Any binary (infix) operator can only relate two things, but typically when you want to print something you actually want to relate three things: the stream, the object or value to print, and the way you want values represented. Oh, sure, you can manipulate this by inserting magic formatting objects, but that's not different from saying all functions take one argument a la Category Theory or Haskell without syntactic sugar. Or you could write a class or function that allows you to tag your object or value by changing its type to a different one so a different operator<< overload is called. In the iostreams design, you're only indirectly selecting which print function to use by manipulating either the stream or the printed object. But in actuality the types of your two arguments are not different, so either you must munge global settings on the stream or "hack" which operator<< overload is selected. And that's the crux of the issue; what you "really want" is a stream API with three arguments: the stream, the object or value, and a pointer to a function (or function object) that takes the value and transforms it to a stream of characters. Why not just pass that function directly and explicitly? Of course that requires something that's not just a single chained infix operator; my thesis being that starting with that quirky choice leads to down a path that ends in a bad design.
- mrozbarry 3y agoI think headers were a great tool from a time before intellisense where developers and teams would include function + comments on how to use it. On the flip side, I think there are still a lot of developers who don't use language servers or use the equivalent of notepad who do rely on separate header files as a means of documentation. That said, it's always been painful to structure inter-related headers and source files to avoid circular dependencies, and if modules truly resolves that, I'm happy to see it.
- cozzyd 3y agoconan2 breaking backwards compatibility with conan1 is... rough. My first conan experience was everything breaks because of that...
- rbanffy 3y agoWouldn’t it make sense to make the BMI file to be compiler neutral? I’m not sure there is an actual need for them to be that intimately tied to the compiler. What information that’s impossible to recreate from the equivalent of a header file is required?
- layer8 3y agoI think they are just an optimization, like precompiled header files. Theoretically they could probably take the form of C++ module interface units without implementation, but that wouldn't gain you that much in terms of compilation time.
- stabbles 3y agoYeah, that'd make a lot of sense. With Fortran mod files I believe it's not even backwards compatible for a single compiler: gfortran. If it's not standardized, you end up recompiling the world, which defies the point.
- humanrebar 3y agoThere is a proposal for this: https://github.com/microsoft/ifc-spec https://github.com/microsoft/ifc-spec Only Microsoft implements that so far. I gather EDG (which powers IntelliSense) has been at least researching support for consuming IFC files. I'm not aware of anyone sponsoring work in Clang or GCC to add IFC support.
- LightFog 3y agoThis BMI compiler dependency seems quite the limitation, surely this was a chance to tidy and unify name mangling. So we can’t import binaries across even minor compiler version changes?!
- gumby 3y agoYou don’t want unified mangling because different compilers make different default decisions on laying out data. While that’s mostly been stamped out in C++ (mixed feelings about that), their library implementations are quite different, which Is a good thing.
- bluGill 3y agoI want unified mangling only if it comes with unified basic types. I want a string (maybe not std::string) that I can use not just in C++, but also rust, ada, python, C#, Java, and whatever other language is hot this week that is willing to provide some guarantees (this might include something like deterministic destruction which could toss some of the above out - something to debate). Likewise I want a list type (like std::vector) that I can put in arbitrary types and access by the above languages. The above quickly forces some form a struct type with a layout, and likely also functions (thus class), though we can debate how much we include.
- marwis 3y agoSeems like you want winrt
- stabbles 3y agoC++ is really becoming easier to use! compiler error: you have to match the flags of that one dependency you're not familiar with and you didn't even compile yourself, rookie!
- maccard 3y agoYouve always needed to do this. If you link against a static library that's compiled with -D_GLIBCXX_ASSERTIONS=1 it needs to be defined in your code too. Same for the CRT flags with MSVC
- stabbles 3y agoYou'd hope that modules would make those issues go away, instead of making them worse. Also, CPPFLAGS ensure the shared code in headers is the same, whereas with modules it feels as if you have to manually bring the compiler itself in the right state.
- pjmlp 3y agoNo, it has been the same since compiled languages exist, modules only improve the packaging and type safety story. CPPFLAGS ensures not such thing when using binary libraries.
- gpderetta 3y agoABI-breaking flags break the ABI. There is no way around that. edit: incidentally, _GLIBCXX_ASSERTIONS is not ABI breaking.
- maccard 3y agoTIL!
- senkora 3y agoYou really just have to compile all of your dependencies yourself (or use pre-compiled distro packages and compile your code with the distro toolchain). Of course, compiling everything yourself is the only way to get LTO to work well, and you probably want that.
- gumby 3y agoSo the bmi file is the replacement for the header file and the module the replacement for the lib? Got it. I’d like to have the option to have a bmi section in the module file itself; that of course would mean that most people would either have to have a writable /lib &. /usr/lib etc or constrain the compiler flags available. I wonder how many people would actually be inconvenienced by the latter.
- humanrebar 3y agoNo. The BMI is more like an object file. It's not likely anyone would ship one. The lib is still the lib. The header files are replaced with module interfaces. It's just that those are probably going to be shipped with some parsing instructions, like required preprocessor flags, the C++ version used, and that sort of thing.
- Alifatisk 3y agoAre there any modern "package manager" for C?
- deleted 3y ago[deleted]
- elitepleb 3y agohttps://xrepo.xmake.io https://xrepo.xmake.io does a decent job at that, but then you're stuck with a modern "build system" too
- humanrebar 3y agoConan, Spack, Nix, vcpkg, not to mention the OS specific ones. All are actively maintained. Modern means different things to different people, so I expect some people will still be looking for more answers.
- rgoulter 3y agoIn my head, these are: - Conan and vcpkg are probably the closest equivalent to other "language package managers" (e.g. Rust's cargo, or NodeJS's NPM). - Spack is typically used in HPC/Scientific Computing domain. - Nix (and Guix) are very powerful (system) package managers; these have enough nifty features that I'd call them "next-generation". Probably also for sufficiently complicated projects, it may be worth mentioning sophisticated build tools like Bazel (which I think manages dependencies in its own way).
- tgamblin 3y agoSpack dev here. Yep it's mainly used in HPC, but we'd love to see more folks using in other areas. There is windows support now (in addition to linux and macOS), which I think was a roadblock for many, and we have gotten some very interest from folks outside the HPC community, e.g. Replica.one, who hope to use it for a software-defined OS for embedded devices: https://www.youtube.com/watch?v=sMxNafpDhng https://www.youtube.com/watch?v=sMxNafpDhng (skip to ~12min for some discussion of Spack, Nix, Guix, and portage)
- boris 3y ago> So far, CMake 3.28 (still a Release Candidate at time of writing) is the only build system that implements dependency scanning and the ability to consume external libraries that provide modules, and the BMIs are built locally rather than distributed. Hm, build2 was able to do this back in 2021: https://build2.org/blog/build2-cxx20-modules-gcc.xhtml https://build2.org/blog/build2-cxx20-modules-gcc.xhtml And it was able to do it for both named modules and header units (the mentioned CMake release can only handle named modules).
- humanrebar 3y agoThere's no way build2 did exactly this back in 2021 given communication APIs for GCC just landed in its main branch a month ago. Clang and GCC, only a year ago or so. How does build2 understand module dependencies without that?
- boris 3y agobuild2 use the module mapper API which was available long before 2021: https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Module-Mapper.html https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Module-Mapper... You can try the examples here https://github.com/build2/cxx20-modules-examples/ https://github.com/build2/cxx20-modules-examples/ with GCC 13 or 12 to confirm, if you wish. > given communication APIs for GCC just landed in its main branch a month ago Can you elaborate on what are these "communication APIs"?
- humanrebar 3y agohttps://wg21.link/p1689 https://wg21.link/p1689 Well, the daemon-based approach won't really work with certain kinds of distributed build and caching systems. I suspect it will work for a lot of use cases though. It's also problematic if you want to parse code without a build system as such. Does every analysis tool and IDE need to add support for that GCC specific API? But the p1689 approach is implemented the same way for all three major compilers. And it should be reasonably adoptable by anything else that might want to work with CMake and other build systems that choose to support p1689.
- vkoskiv 3y agoTotally agree that plain header files have many issues, and they are a common source of headache, but can someone explain to me why replacing plaintext, greppable header files with a seemingly very fragile/unportable binary format is a good idea? Surely I must be missing something? I get the sense that they tried to fix complex issues of header files by introducing more complexity. Is it assumed that new tooling will hide this complexity? All genuine questions, I haven't tried out C++ modules myself at all yet.
- bogwog 3y agoYeah this just sounds like precompiled headers, except the only part they actually standardized was the name.
- boris 3y ago└-- lib |-- cxx | └-- foo.cppm ---> this is a module interface (does `export module foo`) └-- libfoo.a Hm, .../lib/ normally contains architecture-specific files so I wonder what was the rationale behind installing architecture-independent source code (foo.cppm is a source file) there instead of something architecture-independent like .../include/?
- sph 3y agolib on Linux contains library files. On native languages, this means they contain native code, but not all library files are architecture-dependent. See Python files at /usr/lib/python<version> /usr/include on the other hand only is used for C and C++ header files. Modules are not header files.
- boris 3y ago> but not all library files are architecture-dependent. While this is definitely true, the presence in /usr/share/ of many files from library packages on my system (Debian) still suggests that architecture-independent files should not go into /usr/lib/. > See Python files at /usr/lib/python<version> Aren't the compiled (.pyc) files in there architecture-dependent (or could be; genuine question, I have no idea)? > /usr/include on the other hand only is used for C and C++ header files. Modules are not header files. Yes, but it doesn't follow they cannot be installed there if that location is the most suitable from the distribution packaging point of view.
- deleted 3y ago[deleted]
- bogwog 3y agoThat example isn't a linux file system, it's a Conan package. Conan packages are built for a single configuration/architecture, and only used when compiling your project. They live in a local Conan cache folder, and not installed on the system.
- mkoubaa 3y agoConan indulges the fantasy that c++ code is packageable. Sorry, in 2023 the only portable way to distribute c++ libraries is still by source. This is why header only libraries are so desirable.
- rurban 3y agoconan begs to differ. they distribute binary packages for many options: arch, compiler, debug/release, flags, custom, ...
- wredue 3y agoConan can beg to differ all they want. Offering source meets all of those needs without have to rely on a third party guessing your needs.
- rurban 3y agoConan is a cooperate packaging framework, which obviously favors binary packages over source-only. And there's no guessing, all the options are hashed, and the binary is based on this hash. A single option change leads to different binaries. Even in open source it's extremely important to use proper options (arch, flags, deps in which variant, ...). E.g. with something Debian or Redhat they decide on these options. With conan you can decide by yourself. E.g. use better hardening flags or a better libc or a more stable dependency package.
- humanrebar 3y agoPortable in what dimensions? There's no portable way to describe how to build an archive containing C++ source code.
- bogwog 3y agoI've been using Conan for years with great success. I think you might have missed the documentation: https://docs.conan.io https://docs.conan.io
- pornel 3y agoI'm hoping C++ modules take off, because getting rid of naive textual inclusion enables mixing of code from different language versions, even if they're slightly incompatible. It would enable C++ to have an Edition concept like Rust, and be able to start removing warts and poor defaults from the language, without breaking old code, and without needing the whole world to upgrade at once (well, except the modules adoption).
- pipeline_peak 3y agoC++ is getting blue haired language features. Quick, someone make a mascot!