本文简要介绍了 C++ 模块(我们希望是 C++17,但我们必须等待)。模块已经在 CLang 的早期实现中进行了实验性提供,现在 Microsoft 也在 Visual Studio 2015 中提供了它们。我们将看到它们的语法以及如何构建它们,如 Visual Studio 博客 中所述,同时,我们将展示如何使用 conan C/C++ 包管理器创建和使用包含 C++ 模块的包。

一个数学 C++ 模块库和包

首先,我们将创建一个非常简单的数学库,其中包含加法和乘法函数,这些函数将在一个名为 MyMath 的模块中实现,文件名是 mymath.ixx。此扩展名将用于指示 C++ 模块语法

module MyMath;
export double addition(double a, double b){
    return a + b;
}
export double multiplication(double y, int z){
    return y * z;
} 

目前,构建它需要使用命令行调用 MSVC 编译器 cl.exe。这应该在 Visual Studio 命令提示符中完成,或者在加载了 Visual Studio 环境变量的情况下完成。我通常在 Windows 的 cmder 控制台中工作,因此我更喜欢加载环境变量

$ call "%vs140comntools%../../VC/vcvarsall.bat"
$ cl /c /experimental:module mymath.ixx

构建此文件时,编译器将生成典型的目标代码,以及一个名为“MyMath.ifc”的 IFC 文件,它是模块接口描述元数据。如果我们想要构建一个真正的静态库,我们也可以在命令行中进行

$ lib mymath.obj -OUT:mymath.lib

从用户的角度来看,.ifc 文件可以像处理其他库一样进行处理和链接,因此我们只需为这段代码创建一个 conan 包配方,除了 *mymath.ixx 文件之外

from conans import ConanFile, CMake, tools
import os

class VSModulesConan(ConanFile):
    name = "MyMath"
    version = "0.1"
    license = "MIT"
    settings = "os", "compiler", "build_type", "arch"
    exports = "mymath.ixx"

    def build(self):
        param = "x86" if self.settings.arch == "x86" else "amd64"
        # Missing handling of build_type, but lets keep it simple
        vcvars = 'call "%%vs140comntools%%../../VC/vcvarsall.bat" %s' % param
        self.run('%s && cl /c /experimental:module mymath.ixx' % vcvars)
        self.run('%s && lib mymath.obj -OUT:mymath.lib' % vcvars)

    def package(self):
        self.copy("*.lib", "lib") 
        self.copy("*.ifc", "lib") 

    def package_info(self):
        self.cpp_info.libs = ["MyMath.ifc", "mymath.lib"]

现在我们可以将包导出到 conan 本地存储中,以便我们可以从那里使用它

$ conan export memsharded/testing

使用 C++ 模块库

现在,在另一个文件夹中,我们可以创建使用库的项目

#include <iostream>
import MyMath;

int main(){
    std::cout<<"MyModules\n";
    std::cout<<addition(5.1, 3.2)<<"\n";
    std::cout<<multiplication(2.0, 3)<<"\n";
    return 0;
} 

从这段代码构建可执行文件,需要执行以下命令

$ cl /EHsc /experimental:module /module:reference <libpath>/MyMath.ifc <libpath>/mymath.lib  main.cpp 

我们需要库路径,如果我们手动构建了库,它将是我们生成库时的路径。对于 conan 包,使用配方声明对库包的依赖关系并自动执行构建非常方便,即使我们没有为使用者项目创建包。请记住,conan 配方基本上是方便的 Python 脚本

from conans import ConanFile, CMake, tools
import os

class VSModulesTestConan(ConanFile):
    license = "MIT"
    settings = "os", "compiler", "build_type", "arch"
    requires = "MyMath/0.1@memsharded/testing"

    def build(self):
        param = "x86" if self.settings.arch == "x86" else "amd64"
        vcvars = 'call "%%vs140comntools%%../../VC/vcvarsall.bat" %s' % param
        lib_path = self.deps_cpp_info.lib_paths[0]
        libs = " ".join("%s/%s" % (lib_path, lib) for lib in self.deps_cpp_info.libs)
        command = ('%s && cl /EHsc /experimental:module /module:reference %s %s/main.cpp '
                     % (vcvars, libs, self.conanfile_directory, ))
        self.run(command)
        
    def test(self):
        self.run("main")

使用此配方,安装所需的依赖项(在我们的例子中,使用 MyMath C++ 模块构建库)非常简单

$ conan install --build

然后,构建并执行应用程序

$ conan build
$ main
MyModules
8.3
6

结论

如果您想快速测试,您可以

$ git clone https://github.com/memsharded/vs_modules
$ cd vs_modules
$ conan export memsharded/testing
$ conan test_package 

我们一直将 Windows 视为一等公民(以及 Linux 和 OSX),并在 conan 中设计、实现、测试和部署时考虑到 Windows 用户(我们也大约 50% 的时间在 Windows 上进行开发)。

我们希望这篇文章能证明这一点,尽管 Visual Studio C++ 模块构建基础设施处于早期阶段(仅限命令行),但 conan 包管理器能够非常轻松直观地创建和使用包。一旦 C++ 模块在 IDE 和构建系统中获得完全支持,通过改进现有的“Visual Studio”或“CMake”生成器可以进一步简化这一点。

我们也非常高兴能够在 Visual Studio 中测试这个令人惊叹的功能,毫无疑问,Microsoft 最近在 C++ 方面做了惊人的工作,并且非常期待看到 C++ 模块成为主流。