内容简介:January 22nd, 2020C++20 is right around the corner. Along with the new standard comes the much anticipated Modules feature! The compiler teaminitially announced that we were working on the Modules TS back in 2017 and since then we have been hard at work im
Cameron
January 22nd, 2020
C++20 is right around the corner. Along with the new standard comes the much anticipated Modules feature! The compiler teaminitially announced that we were working on the Modules TS back in 2017 and since then we have been hard at work improving the feature and improving compiler conformance around this feature. We finally feel it is time to share some of the progress we have made on the conformance front for Modules.
What’s new?
- are a new form of translation unit which act like portable PCHs.
- Context sensitive
module
andimport
keywords provide users with more flexibility when using these terms as identifiers in code. - Global module fragmentis a way of separating non-modular code from module interface code when composing a module interface.
- are a type of module interface which compose a larger module interface.
Header Unit Support
In C++20 [module.import]/5 describes the import of a new translation unit type, the header unit. The semantics of this type of import are further elaborated on in [module.import]/5 and one of the more important pieces of information is that macros defined in that imported header are also imported:
myheader.h
#pragma once #include <cstdio> #define THE_ANSWER 42 #define STRINGIFY(a) #a #define GLUE(a, b) a ## b
main.cpp
import "myheader.h"; int f() { return THE_ANSWER; } int main() { const char* GLUE(hello_,world) = STRINGIFY(Hello world); std::printf("%s\n", hello_world); }
The sample above can be compiled using the new /module:exportHeader
switch:
$ cl /std:c++latest /W4 /experimental:module /module:exportHeader myheader.h /Fomyheader.h.obj $ cl /std:c++latest /W4 /experimental:module /module:reference myheader.h:myheader.h.ifc main.cpp myheader.h.obj
Notice the use of /module:exportHeader
, the argument to this option is a path (relative or absolute) to some header file. The output of /module:exportHeader
is of our .ifc format. Meanwhile, on the import side, the option /module:reference
has a new argument form which is <path-to-header>:<path-to-ifc>
and either one or both of the paths expressed in the argument to /module:reference
can be relative or absolute. It is also important to point out that without the /Fo
switch the compiler will not generate an object file automatically, the compiler will only generate the .ifc.
One other intended use case of the /module:exportHeader
is for users (or build systems) to provide a text argument to it which represents some header name as the compiler would see. A quick example is:
$ cl /std:c++latest /EHsc /experimental:module /module:exportHeader "<vector>" /module:showResolvedHeader <vector> Note: resolved <vector> to 'C:\<path-to-vector>\inc\vector'
This use of /module:exportHeader
enables the compiler to build header units using the header search mechanism as if the argument were written in source. This functionality also comes with a helper switch, /module:showResolvedHeader
, to emit the absolute path to the header file found through lookup.
Note to readers: there is a known limitation with /module:exportHeader
and its interaction with /experimental:preprocessor
these two switches are currently incompatible and will be resolved in a future release.
Context Sensitive module
and import
keywords
In the Modules TS both module
and import
were treated as keywords. It has since been realized that both of these terms are commonly used as identifiers for user code and hence a number of proposals were accepted into C++20 which add more restrictions as to when module
and import
are keywords. One such proposal was P1703R1 which adds context sensitivity to the import
identifier. Another such proposal—but one which is not yet accepted—is P1857R1 . P1857R1 is interesting in that it is the most restrictive paper in defining when module
and import
are keywords or identifiers.
As of 16.5 MSVC will implement both P1703R1 and P1857R1. The result of implementing the rules outlined in these two papers is that code such as:
#define MODULE module #define IMPORT import export MODULE m; IMPORT :partition; IMPORT <vector>;
Is no longer valid and the compiler will treat the macro expansion of both MODULE
and IMPORT
as identifiers, not keywords. For more cases like this please see the papers, in particular P1857R1 provides some useful comparison tables describing the scenarios affected by the change.
Global Module Fragment
Since the merging of Modules into C++20 there was another new concept introduced known as the global module fragment. The global module fragment is only used to compose module interfaces and the semantics of this area borrows semantics described in the Modules TS regarding entities attached to the global module. The purpose of the global module fragment is to serve as a space for users to put preprocessor directives like #include
‘s so that the module interface can compile, but the code in the global module fragment is not owned by or exported directly by the module interface. A quick example:
module; #include <string> #include <vector> export module m; export std::vector<std::string> f();
In this code sample the user wishes to use both vector
and string
but does not want to export them, they are simply an implementation detail of the function they wish to export, f
. The global module fragment in particular is the region of code between the module;
and export module m;
. In this region the only code which can be written are preprocessor directives; #if
and #define
are fair game. It is important to note that if the first two tokens of the translation unit are not module;
the interface unit is treated as though a global module fragment does not exist and this behavior is enforced through [cpp.global.frag]/1 .
Module Partitions
Module partitions provide users with a new way of composing module interface units and organizing code of a module. At their very core, module partitions are pieces of a larger module interface unit and do not stand on their own as an interface to import outside of the module unit. Here is a quick example of a simple module interface which uses partitions:
m-part.ixx
export module m:part; export struct S { };
m.ixx
export module m; export import :part; export S f() { return { }; }
main.cpp
import m; // 'm' is also composed of partition ':part'. int main() { f(); }
To compile the sample:
cl /experimental:module /std:c++latest /c m-part.ixx cl /experimental:module /std:c++latest /c m.ixx cl /experimental:module /std:c++latest main.cpp m.obj
Notice that we did not explicitly add /module:reference
to any invocation of the compiler, this is because we have introduced a naming scheme for module partitions which ease the use of the feature—just like we have for normal module interface units where the filename represents the module name directly. The pattern that module partitions use is <primary-module-name>-<module-partition-name>
. If your module partitions follow that pattern the compiler can automatically find interface units for partitions. Of course, should you actually want to specify the module interfaces on the command line simply add the appropriate module:reference
arguments.
The standard refers to partitions in general as being interface units [module.unit]/3 , however there is one exception and that is what we refer to as an “internal” partition. These internal partitions are not interfaces and only serve to facilitate the implementation details of a module unit. It is expressly ill-formed to export an internal partition (see translation unit 3 in section 4 of [module.unit]). MSVC implements the creation of internal partitions through a new switch /module:internalPartition
. An example of using an internal partition:
m-internals.cpp
note the .cpp extension
module m:internals; void g() { } // No declaration can have 'export' in an internal partition.
m.ixx
export module m; import :internals; // Cannot export this partition. export void f() { g(); }
To compile this interface:
cl /experimental:module /std:c++latest /module:internalPartition /c m-internals.cpp cl /experimental:module /std:c++latest /c m.ixx
As previously mentioned, the :internals
partition can only be used to implement parts of the module interface m
and cannot contribute to it directly.
Closing Thoughts
C++20 is bringing a lot of new concepts (literally and figuratively) to C++ and Modules are one of the largest contributors to how we will write code differently in the future. These MSVC conformance changes will help users facilitate the transition into thinking about how we organize and reason about interfaces to our APIs. As with all of our preview features the switches and compiler behavior with respect to modules are subject to change once we are ready to declare the toolset C++20 complete.
We urge you to go out and try using MSVC with Modules. 16.5 is available right now in preview through the Visual Studio 2019 downloads page!
As always, we welcome your feedback. Feel free to send any comments through e-mail at visualcpp@microsoft.com or through Twitter @visualc . Also, feel free to follow me on Twitter @starfreakclone .
If you encounter other problems with MSVC in VS 2019 please let us know via theReport a Problem option, either from the installer or the Visual Studio IDE itself. For suggestions or bug reports, let us know through DevComm.
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。