They're not supported in cmake (unless you set CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API to 2182bf5c-ef0d-489a-91da-49dbc3090d2a if you're on the same version of cmake as I am).
My experience is that no IDE's support them properly, so either you need to ifdef out for intellisense, or go back to no tooling support.
The format breaks every update, so you can't distribute precompiled modules.
Last but definitely not least, they're slower than headers.
So, we've got a feature that we started talking about 11 years ago, standardised 3 years ago, not implemented fully, has no build system support, and is a downgrade over the existing solution from every real world use case I've seen. What a mess.
The other points (mainly integration points) are definitely valid though.
Important question: Who are you disappointed in?
Keep in mind that the ISO committee does not employ engineers and for international treaty reasons, it reasonably cannot.
Point being, who should be doing this work? Are you talking to them about your expectations? Are you providing actual support to implementations that are public goods (i.e., open source compilers)?
> Keep in mind that the ISO committee does not employ engineers and for international treaty reasons, it reasonably cannot.
Engineers not being employed by the committee doesn't meani can't hold them to a standard. The standards committee have shown that they're more interested in freesing popular libraries and putting them in the standard library. It's clear that major changes are preferred as library changes not language changes (ranges should be a language feature, as should span and optional).
> who should be doing this work?
I'm not sure what you're getting at here. The standards committee should have used some time between 2004 (when this was proposed first) and 2019 to get this done. The compiler vendors, 4 years later , should have made this usable. The build systems are at the mercy of the compiler vendors, but cmake has had 3-4? years to get this in.
> Are you talking to them about your expectations? Are you providing actual support to implementations that are public goods (i.e., open source compilers)?
It's not fair for you to suggest that it's my fault this is a mess, 15 years on.
To answer your question, I've tried multiple times over the last 3 years, hit show stopping bugs and issues, and have found them already reported in clang's issue tracker or the VS feedback forums. I've spoken with developers here and on Reddit about issues with MSVC, and cmake.
This is especially frustrating for organizations ostensibly paying vendors for high quality compilers.
Downside is that at the moment you cant mix normal header STL with module STL in the same project (msvc), so its for cleanroom small projects only. I expect the second you can reliably use that almost everyone will switch overnight just from how fast of a speed boost it gives on the STL vs even precompiled headers.
> I expect the second you can reliably use that almost everyone will switch overnight just from how fast of a speed boost it gives on the STL vs even precompiled headers.
I look forward to that day, but it feels like we're a while off it yet
When will they fix this? People keep using C to this day because these unstable binary interfaces make it so no one can reuse software. Can't write a library in almost any other language and expect it to just work. To this day, C is the lowest common denominator of software because of stuff like this.
Even flag selections make incompatible BMIs. So if one TU uses C++20 and another uses C++23, they'll each need their own BMI of anything they import.
FD: CMake developer that implemented the modules support
It is my opinion that hand-coded Makefiles are unlikely to handle modules well. They have the same required strategy as Fortran modules where the (documented!) approach by Intel was to run `make` in parallel until it works (basically using the `make` scheduler to get the TU compilation order eventually). Of course, there's no reliable way to know if you found a stale module or anything like that if there's a cycle or a module rename leaving old artifacts around.
There is the `makedepf90` tool that could do the "dynamic dependencies" with a fixed-level recursive Makefile to order the compilations in the right order (though I think you may be able skip the recursive layer if you have no sources generated by tools built during the build).
Anyways, this strategy fails if you want implementation-only modules, modules of the same name (that don't end up in the same process space; debug and release builds in a single tree are the more common example though), use modules from other projects (as you need to add rules to compile their module interfaces for your own build).
Instead, some sort of dependency graph amongst the .ccm needs to be encoded in the Makefiles. There is an ISO paper describing how to query a compiler for this info (see https://wg21.link/p1689). Other papers sketch out how one might incorporate that into a dynamic Makefile graph, but so far nobody seems interested in going that far. Switching to a more actively maintained build system is probably the interesting alternative.
If you want to learn more about how build systems can support C++ modules, see this blog post by the CMake maintainers. The principles translate to most (all?) nontrivial build systems.
"As far as I know, header files came to be because when C was built disk space was at a prime and storing libraries with their full source code would be expensive. Header files included just the necessary to be able to call into the library while being considerably smaller."
The modules/packages that were built from C/C++ for example in Python and maybe other scripting languages those were all compiled with the inclusion of the .h files. for exactly the reason that the OP states...for efficiency. Header files should really only include definitions, and when you compile it's tight code using only what you included.
Wouldn't modules include everything? isn't that like saying:
import *
as opposed to say:
Import module.package
I'm just confused how this will help statically typed languages like C/C++ into maintaining and compiling to very efficient machine or executables.
Someone enlighten me.
In Python, import is an act of bringing in everything into the namespace (under its own or otherwise).
In compiled languages, it is the act of making it available within this compilation unit. Things that are unused are stripped out and only what you either use or re-export have an effect. That is of course for static binaries, for dynamic linkage none of it really matters.
In fact modules can be more efficient than headers, because the way C/C++ work is that everything within the header file is verbatim copied into the file referencing it. This isn’t true with modules.
This claim doesn't make sense to me. You don't want and can't include the implementation in multiple C compilation units because the symbols from them would conflict. I suppose if you include the existence of shared libraries as a means of saving disk space the assertion isn't wholly wrong, but I've never see anyone make such a claim before.
Headers exist even within a C project so that the various parts of it can share the function prototypes and struct definitions while being independently compiled.
As far as I understand it, they have a tool that can parse all their (C) files, figure out what each file declares and what declarations it depends on, and then autogenerate a header for every file with just the declarations it requires.
It seems like a sensible approach, but nobody besides sQLite and Fossil (which comes from the same developers) seems to be using it, either in C or C++, which leads me to suspect that there are caveats I don't know about. Is there a reason why this is a bad idea?
[1] https://fossil-scm.org/home/doc/trunk/tools/makeheaders.html
You guys OK in C++ land?
So you can’t really rely on extensions to be sufficient.
Many build systems and compilers do assume specific extensions imply a file type by default, but those can often be configured to arbitrary extensions as well.
And really even among the non-standard extensions it's like 95% .cpp, 4.9% .cc and 0.1% .c++. Ignoring the weirdos it's de facto .cpp or .cc. They could standardise that too if they really wanted and there was a benefit.
This sounds like one of those things that often happens where people worry about technically possible but totally insignificant risks, and insist on handling them because they feel proud of having thought of them.
Not enough YAGNI.
But it's also an important hint to the preprocessor to make pre-build dependency scanning faster. You don't want to have to compile all possible source files and then throw a bunch of that work away once you find there are module dependencies in the source files. Just having a magic file extension is not enough to clarify that.
A C++ packaging standard could help there, but C++ started with modules, probably because they seemed simpler.
I programmed in Modula 2; that definitely didn't need make.
The module definitions need to be parsed and semantically analyzed, so the compiler has to be the build tool.
Can mobiles be nested? (I'm thinking java.util.StringTokenizer.) If so, has a module hierarchy (as for Java) been defined for all STL standard functionality?
Are their any compilers that implement C++20 in full in 2023?
I guess we could instead mandate that module names have to match the namespaces inside (i.e., mandate what modules are named up front), but it's also common to have C++ namespaces be reopened for various reasons (like adding to overload sets in supported ways).
Anyway, bottom line is that modules and namespaces need to have M:N relationships, though in practice, it should be Bad Practice to get too creative there. Nobody has added namespace/module matching rules to CppCoreGuidelines or clang-tidy yet, but right now is a good time for some ideas in that space.
"We'd like this new feature to be good, but we have to cripple it for adoption/back-compatibility reasons". This is the entire story of C++, sadly :(
Modules being orthogonal to namespaces means:
1. Bad Surprise for people first using modules, leading to incomprehension as this is different from every other language
2. Occasional Bad Surprises for people using modules even after the first time, as people will export stuff outside of namespaces/in the wrong namespace from time to time
3. Some code using the convention that the namespaces in modules follow the module "hierarchy" (which is also entirely a conventional concept because `.` is not treated as a special character) and some code not following that convention, either because it was hastily ported to modules or because the authors elected not to care for this particular convention. Now enjoy not knowing that when importing a module from a third-party library.
Unfortunately the orthogonality to namespaces, in the name of "facilitating migration", cripples the language forever and actually reduces the motivation for migrating to modules (because it doesn't remove footguns). Doesn't help that the whole module feature is designed in this spirit. I'm lucky enough to have migrated to greener languages, but I don't think I would find it worth it to migrate to modules if I were still in the position of deciding what C++ features to introduce in my team.
> Ideally one could find/replace includes with analogous imports, a bit at a time.
Make a tool like `cargo --fix`, or something. Ah no, we can't, because of headers (and more generally the preprocessor), ironically :(
#import foo
foo::bar();
vs
#import foo::bar
bar();Now, I'm sure there's more nuance to actually standardise this, but given you already need to replace #include with #import, this isn't going to catch anyone off guard.
I'm not sure what the official rationale is, but I feel that would cause great namespace pollution. Some companies use one namespace per "logical" unit of code (e.g. a subproject), not for each small piece of code (e.g. a small module implementing a couple of classes.
Not to mention that if it were the case, things like a "Boost incubator" library would be in a separate top-level namespace as it could not "inject" itself into `import boost;`.
However, "deducing this" has the side-effect of allowing to explicitly specify the "this" parameter in the argument list. The syntax looks the same as Rust, and the convention of calling this parameter "self" is taken from "other languages" (Python, Rust, ...)[0].
Similarly, it could be argued that the ability to pass the object by value in method is lifted from Rust. That's how I understand the GP comment anyway.
[0]: https://devblogs.microsoft.com/cppblog/cpp23-deducing-this/ "You don’t have to use the names Self and self, but I think they’re the clearest options, and this follows what several other programming languages do."
Before, you had to put template classes into headers, so you effectively had to recompile, for example, the class std::vector<T>, for each file you included the header <vector> in, because a new compiler process compiles each .cpp file from scratch, knowing nothing. If two separate files instantiated std::vector<int>, then it would be instantiated and defined separately at compile time, and at link time, one of the definitions would be discarded. The only way to speed it up before modules was to use pre-compiled headers, which are compiler-specific.
Modules let the compiler share state across files, so it doesn't have to recompile template instantiations unnecessarily.
I think this guy's blog explains it well: https://blog.ecosta.dev/en/tech/explaining-cpp20-modules
> The only way to speed it up before modules was to use pre-compiled headers, which are compiler-specific.
Modules are compiler specific too, unfortunately.
D has a simple and effective module system. It even treats unmodified .c and .cpp files as modules (though the C and C++ files are unaware of that!). D's builtin C compiler can even import other C files as modules.
Both C and C++ should have just copied it.
modern languages dont adopt them because they are terrible. a header import just dumps all the declarations into the global scope, so you have no idea where function or type usages comes from.
In modules, the implementation completely dominates, and it's impossible to figure out what the module API looks like from looking at the source. You need an IDE to generate an outline for you, which frankly sucks.
For C at least, headers are pretty much perfect because they are supposed to only contain public API declarations. It's just C++ that messed everything up with the inline and template keywords, which required implementation code to move into header files.
See: `cargo doc` that generates nice HTML documentation. See also language servers that provide completion using the same documentation comments.
Meanwhile headers, on top of the forced duplication of signatures between header and implementation, and the associated cost, is also implemented on terms of the preprocessor and textual inclusion. This compilation model makes code analysis much harder than necessary, because it becomes hard to know which piece of the preprocessed compile unit comes from which header.
This makes it hard to implement e.g. "remove unnecessary import" or "import symbol automatically" functionalities that are the bread and butter of module-based languages. Also impacts automatic refactoring negatively
One great thing - it's nice to have an interface[1] which already has tooling to let other languages use your private, hidden and compiled implementation.
If those C++ libraries that almost all of Python scientific work is built on had used modules[2] (or something similar) instead of headers, interfacing Python to those libraries would have been an immense effort.
Even with modules existing now, if you're writing something core and foundational in C++, it's not getting adopted like Numpy, BLAS, etc unless you also provide header files.
And that's just Python. Think of all the other languages that rely on performance critical code written in C++, and used with a C-only interface.
[1] Of course, your interface can't be a C++ interface either, so there's that.
[2] If modules were around at the time.
Textual inclusion also allows a number of things modules do not.
The main disadvantage that they have, that they're stateful, is also true of module systems in many languages (e.g. python).
Edit: Ok I get it, disagreement is an encouraged reason for downvotes on HN. If a reason for an opinion is given, a refuting response is preferred, but in this case, no reason was given.
No, please. If you're using C++20 modules, you also owe it to yourself to use a modern build system...
Edit: it seemed like I fired right into a Makefile-shaped hornet's nest.
The thing is that they're heavily implementation-specific things. Two of the biggest compiler vendors pushed the model that worked best for their implementation. The Microsoft model won the committee politics, which is why they're the only ones with an implementation. Too bad most C++ developers don't really use Microsoft software to begin with.
They should have just stuck with a simple PCH-but-you-don't-have-to-include-it-first model. That would have been straightforward and immediately useful to everybody.
Clang had another model that was rejected, and most of the years in committee were actually about finding a compromise between Microsoft's and Clang's approach. In the end, Clang wasn't particularly happy about the end result either.
That’s not a criticism necessarily , but just to add that I think they likely have a more agile code base as a result.