Home > OS >  What is the point of two types of module files (interface and implementation) in C 20?
What is the point of two types of module files (interface and implementation) in C 20?

Time:12-07

When I want to export something I write export void foo(); I can implement it in the same module file or do it in a separate one. But what is the point of formally distinguishing these files (export module mymodule vs module mymodule) when, anyway, I can have any number of the latter type. Wouldn't be enough to just put export keyword before the thing I want to make public and not need to bother with special interface files?

CodePudding user response:

Module implementation units can use things with module linkage (neither export nor static) in their module’s interface. If all module units were both interface and implementation units, some mechanism would be needed to deal with the circularity of this access.

The way this is implemented is that module-linkage entities are included in the compiled module interface file: it is helpful to the implementation to know immediately whether that sort of processing is needed for a translation unit. (This is similar to needing to know to what module a file belongs in order to mangle its symbol names properly.)

Moreover, requiring that all parts of the module interface be declared upfront (in the primary interface or in an interface partition that it recruits) avoids needing to “link” the interface results of multiple independent module units into one CMI to be consumed by importers. The fact that module implementation units can’t affect importers except via linking in different symbol definitions is also beneficial to the build system: importers do not need to be recompiled when module implementation units but not interface units change.

CodePudding user response:

At some point, the build system sees that some file says import MyModule;. When it sees that, the build system needs to go find the module for MyModule.

If MyModule has not yet been built, the build system needs to build it. To do that, it has to (among other things) scan all of the known source files in your project to see which ones are used to build MyModule. But the most important thing is that it needs to figure out which file it specifically needs to build in order for import MyModule to work right now.

That process works best and fastest if the system only needs to look for a single file to build (this way, the system can pre-process everything with a quick scanner to find all of those files). So the module system provides that: for any particular module, there is the primary module interface which defines everything that is exported by a module. Building that module may provoke the compilation of other modules, but we know which file has to be finished building before import MyModule can work.

Now sticking everything a module can export into just one file is not the best idea. So in many cases, you'll have multiple files that export stuff, and you'll export import them in your main MyModule primary interface file. But since module names are global, we don't want dozens of tiny module names cluttering up the namespace.

Enter module partitions: These are module interface files whose names are namespaced within a specific module. Module interface files can include other partitions, but only those within the same module. And obviously, the graph of partition inclusion must be acyclic.

But that leaves us with a small problem. Let's say you have a partition that defines a class that gets exported to the primary module interface. But you don't want to put the implementation of those member functions in that partition file. So... where does it go?

I mean, you could put it in another partition that doesn't get imported by anybody. But if that partition doesn't get imported... why bother giving it a partition name? It'd be best if you can communicate immediately that this "partition" cannot be included.

Enter module implementation units. They are part of a specific module, and therefore they can import partitions of that module. But they cannot themselves be imported by anyone.

That's what they're for.

But note that the build system knows that it doesn't need to build module implementation units in order to completely build the module. It only needs to build the primary module interface file and any partitions included by it (directly or indirectly). This allows module rebuilding to be as fast as possible if you put your implementations into implementation units.

Lastly, module implementation units (and interface units) have access to any names that they import from a partition which the partition does not export. These module-local names are only accessible within the module.

  • Related