C++ 20 Module

这篇具有很好参考价值的文章主要介绍了C++ 20 Module。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

头文件包含一直是C/C++的传统,它使代码声明与实现分离,但它有一个非常大的问题就是会被重复编译,拖累编译速度。

通常一个标准头文件iostream展开后可能达几十万甚至上百万行。笔者使用下面的示例进行测试,新建一个main.cc,内容如下:

#include <iostream>

int main(int argc, char* argv[])
{
    return 0;
}

然后分别使用g++和clang++来测试行数:

g++ -E main.cc | wc -c 
1003912
clang++ -E main.cc | wc -c 
999191

随着C++ 20 Module的出现以及各编译器对其的逐步实现,C++也能进行模块化编程,提高编译速度了。

由于历史原因,现有以头文件形式组织的C++代码,不会在短时间内消失,这种情况将持续相当长的时间,也许是几年,十几年,甚至几十年或者更长,所以目前的C++依旧可以在Module中包含头文件,让之与模块(Module)共存,方便使用现有代码。

目前主流的C++编译器有GCC、Clang和MSVC,各个编译器实现的进展不一,使用的命令行参数也不一样,为了简单起见,笔者先以GCC编译器的命令行和Makefile为例来介绍模块的基本写法及编译,再介绍Clang和MVVC使用CMake来编译项目。

本文使用的各编译器版本为:
GCC 13.2.0
Clang 17.0.6
MSVC 19.38.33134.0,即VS2022 17.8.4

CMake版本为:3.28.1

一、模块基础

1. 定义模块

libA.cpp:

export module libA;

export int plus(int x, int y)
{
    return x + y;
}

2. 使用模块

main.cpp

import libA;

int main(int argc, char *argv[])
{
	plus(1, 2);
	return 0;
}

3. 编译:

g++ -std=c++20 -fmodules-ts -c libA.cpp
g++ -std=c++20 main.cpp -c main
g++ -std=c++20 libA.o main.o -o main

需要先编译libA,再编译main

二、模块进阶

1. 接口与实现分离

前面的libA模块代码全部在一个文件中,当代码比较多的时候,清晰度就会下降,可以使用接口与实现分离的形式:

api.cpp:

export module libA;
export
{
int plus(int x, int y);
}

plus.cpp

module libA;

int plus(int x, int y)
{
    return x + y;
}

编译:

g++ -std=c++20 -fmodules-ts -c api.cpp
g++ -std=c++20 -fmodules-ts -c plus.cpp
g++ -std=c++20 main.cpp -c main
g++ -std=c++20 api.o plus.o main.o -o main

注意编译顺序,一定是要先编译模块接口(声明)文件api.cc,再编译模块定义(实现)文件plus.cc,否则会报错:

libA: error: failed to read compiled module: No such file or directory
libA: note: compiled module file is 'gcm.cache/libA.gcm'
libA: note: imports must be built before being imported
libA: fatal error: returning to the gate for a mechanical issue
compilation terminated.

C++ 20 Module,编程语言,# C/C++,c++20,module,cmake,makefile,gcc,clang,msvc

2. Module Partition(模块分区)

当一个模块功能比较多时,C++支持将模块进行分区,每个文件只是模块中的一部分,这样可以将模块的接口与实现进一步分离。

libA/minus.cpp

export module libA:minus;

export int minus(int x, int y)
{
    return x - y;
}

libA/plus.cpp

export module libA:plus;

export int plus(int x, int y)
{
    return x + y;
}

libA/test.cpp

export module libA:test;
import <iostream>;
#include <string.h>

export class CTest
{
  public:
	CTest()
	{
		printf("CTest()\n");
	}
	void foo()
	{
		printf("foo\n");
	}
};

libA/z.cpp

export module libA;

export import :plus;
export import :minus;
export import :test;

在接口文件中引用分区模块时不写主模块名,只写分区名。在GCC中支持写上主模块名,但Clang与MSVC都不支持,所以通用写法是不写主模块名。

编译:

g++ -std=c++20 -fmodules-ts -xc++-system-header iostream
g++ -std=c++20 -fmodules-ts -c libA/minus.cpp
g++ -std=c++20 -fmodules-ts -c libA/plus.cpp
g++ -std=c++20 -fmodules-ts -c libA/test.cpp
g++ -std=c++20 -fmodules-ts -c libA/z.cpp
g++ -std=c++20 main.cpp -c main
g++ -std=c++20 libA/minus.o libA/plus.o libA/test.o libA/z.o main.o -o main

这里同样需要注意编译顺序,先编译系统级头文件,再编译自定义模块。

GCC可以使用import <iostream>;来引用标准库头文件,但是需要先手动编译,第一句即是,引用得的标准库头文件越多,自己手动编译得也越多。注意:有些标准库头文件目前还不能使用import来引用,将头文件编译为模块时会报错。为了最大程度上与其它编译器兼容,目前不建议使用import来引用标准库头文件

自定义模块必须先编译各分区的实现(minus.cpp、plus.cpp、test.cpp),最后编译模块的接口(z.cpp)。

当文件比较多的时候,使用命令行直接一个个文件编译比较慢,也容易出错,所以可以改用Makefile来编译,编写Makefile如下:

CXX := g++
CXXFLAGS := -std=c++20 -fmodules-ts -gdwarf-4
Target := main

SOURCE := $(wildcard libA/*.cpp)
SOURCE += $(wildcard *.cpp)

OBJS := $(addsuffix .o, $(SOURCE))

all: $(Target)

$(Target): std $(OBJS)
	$(CXX) $(CXXFLAGS) $(OBJS) -o $(Target)

std:
	$(CXX) $(CXXFLAGS) -xc++-system-header iostream
	$(CXX) $(CXXFLAGS) -xc++-system-header cmath
	
%.cpp.o: %.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@

clean:
	rm $(OBJS) gcm.cache $(Target) -rf

此时只需要执行make即可编译项目,make clean清除生成的文件。为了让Makefile遵循前面的编译顺序,笔者在文件命名时即按字母顺序进行了相应的排序,所以$(wildcard libA/*.cpp)得到的文件顺序是符合要求的。

为了让lldb也可以调试生成的程序,添加了-gdwarf-4选项。

当源文件有修改,编译时可能会出现CRC不匹配的错误,如下:
C++ 20 Module,编程语言,# C/C++,c++20,module,cmake,makefile,gcc,clang,msvc
需要执行make cleanmake即可。

3. 子模块

子模块与分区模块非常像,只是模块分区使用冒号:分隔,而子模块使用点号.分隔,同时在引入子模块时,必须带有主模块名,即主模块名.子模块名

libA/test.cpp

export module libA.test;
import <iostream>;
#include <string.h>

export class CTest
{
  public:
	CTest()
	{
		printf("CTest()\n");
	}
	void foo()
	{
		printf("foo\n");
	}
};

libA/z.cpp

export module libA;

export import :plus;
export import :minus;
export import libA.test;

4. 私有模块

私有模块必须在主模块或者子模块中写,不能写在分区模块中,格式为:

module :private;

其后的所有内容都将作为模块的私有部分。

注意:目前GCC编译器还不支持私有模块:

sorry, unimplemented: private module fragment

5. 引用头文件

文章开头有说过,头文件与模块方式将长期共存,模块也可以引用头文件,可以使用全局模块的方式,也可以在模块内引用。一般在全局模块中引用,模块内引用可能会报一些意想不到的错误。

全局模块引用:

module;
#include <iostream>
export module libA:plus;

模块内引用:

export module libA:plus;
#include <stdio.h>

三、Clang与MSVC使用CMake来编译项目

前面的示例中,使用了GCC的命令行方式或者Makefile来编译C++模块代码,但是Clang与MSVC使用的参数与GCC不一样,也比GCC复杂,Clang与MSVC在默认情况下都要求模块文件使用新加的扩展名,比如Clang为.cppm.ccm.cxxm, .c++m,MSVC为.ixx。同时,针对模块分区编译的输出文件也有要求,Clang与MSVC都要求为主模块名-分区名.扩展名的格式,Clang扩展名为pcm,MSVC为ifc。具体信息:
GCC可以参考:https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Modules.html
Clang可以参考:https://clang.llvm.org/docs/StandardCPlusPlusModules.html
MSVC可以参考:https://learn.microsoft.com/zh-cn/cpp/cpp/modules-cpp?view=msvc-170

三大编译器不统一,对开发人员来说确实是一件痛苦的事。

好消息是CMake已经支持Clang和MSVC编译器的C++ Module了,而GCC由于还没有实现扫描模块依赖的功能,所以CMake还无法提供支持。

下面还是以前面的示例来介绍使用CMake来编译项目。

在顶层目录创建CMakeLists.txt

cmake_minimum_required(VERSION 3.28)
project(main)

set(CMAKE_CXX_STANDARD 20)
add_subdirectory(libA)

add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE libA)

在libA目录创建CMakeLists.txt

cmake_minimum_required(VERSION 3.28)
project(libA)

set(CMAKE_CXX_STANDARD 20)

aux_source_directory(. SOURCE)
#这里需要去掉路径中的./
string(REPLACE "./" "" SOURCE "${SOURCE}")

add_library(${PROJECT_NAME})
target_sources(${PROJECT_NAME}
  PUBLIC
    FILE_SET cxx_modules TYPE CXX_MODULES FILES
    ${SOURCE}
)

注意:cmake_minimum_required(VERSION 3.28)一定要是3.28及以上,且需要去掉模块路径中的./

为了方便CMake处理模块,建议将同一个模块放在一个目录中,该目录中所有文件都添加到target_sources的C++模块集内。文章来源地址https://www.toymoban.com/news/detail-818396.html

到了这里,关于C++ 20 Module的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用C++编写自己的编程语言CASM

    CASM帮助文档: CASM解释器讲解视频: 我独自开发了一种编程语言:CASM CASM解释器源代码(剪贴板有问题,总是粘贴成一行,哪位大佬帮帮我……): #include iostream #include cstring #include string #include cstdio #include map #include queue #include stack #include cstdlib #include list #include \\\"hint.hpp\\\" us

    2024年02月10日
    浏览(40)
  • C#编程语言的优势与C++对比

           C#语言是由 C/C++演变而来的,是微软推出的一种基于.NET框架的、面向对象的高级编程语言。以.NET框架类库作为基础,拥有类似Visual Basic的快速开发能力。简单易学,入门超快,减少了烦人的指针,有统一的操作符/修饰符/运算符,使用起来极其舒心。         对于

    2024年02月09日
    浏览(32)
  • 编程语言比拼之Java VS C++

    学Java还是C++?   Java和C++都是非常受欢迎的编程语言,各有各的优势和适用场景。以下是对它们的简要比较: 性能:C++通常被认为是一种更高效的编程语言,适用于对性能要求较高的应用程序,如游戏开发、嵌入式系统和高频交易等。C++具有更接近底层的控制能力,允许开发

    2024年02月13日
    浏览(26)
  • [编程语言][C++][Qt]单独添加UI文件

    不知什么原因,Qt Creator并不是很完美很智能。当先写好界面类的头文件和源代码文件后,我们再添加用于可视化界面设计的UI文件时,会出现一些问题。 当使用CMake管理项目时,CMake会读取 CMakeLists.txt 文件来确定各种项目设置。需要把 MainWindow.ui 包含进项目时,在 CMakeLists.

    2024年02月07日
    浏览(35)
  • C++ 编程入门指南:深入了解 C++ 语言及其应用领域

    C++ 是一种跨平台的编程语言,可用于创建高性能应用程序。 C++ 是由 Bjarne Stroustrup 开发的,作为 C 语言的扩展。 C++ 为程序员提供了对系统资源和内存的高级控制。 该语言在 2011 年、2014 年、2017 年和 2020 年进行了 4 次重大更新,分别为 C++11、C++14、C++17 和 C++20。 C++ 是世界上

    2024年03月21日
    浏览(38)
  • 【C/C++】C语言开发者必读:迈向C++的高效编程之旅

    🧑 作者简介 :阿里巴巴嵌入式技术专家,深耕嵌入式+人工智能领域,具备多年的嵌入式硬件产品研发管理经验。 📒 博客介绍 :分享嵌入式开发领域的相关知识、经验、思考和感悟,欢迎关注。提供嵌入式方向的学习指导、简历面试辅导、技术架构设计优化、开发外包等

    2024年03月20日
    浏览(40)
  • 编程语言:微软 Azure CTO 表示,是时候停止在新项目中使用 C 和 C++

    Azure CTO Mark Russinovich 说,业界应该将 C 和 C++ 语言视为“已弃用”。 Windows 11 22H2:如何获得微软最新的操作系统更新以及接下来会发生什么 Microsoft Azure 的首席技术官 Mark Russinovich 表示,出于安全性和可靠性的考虑,开发人员应避免在新项目中使用 C 或 C++ 编程语言,而应使用

    2024年02月06日
    浏览(41)
  • 深度剖析qt cmake 的qt_add_qml_module函数

    qt_add_qml_module 函数是一个高层次的 CMake 函数,用于创建和管理 QML 模块。它简化了将 QML 代码与 C++ 代码集成以及与其他资源文件集成的过程。这个函数旨在用于 Qt 6 和更高版本的项目。 在调用 qt_add_qml_module 时,它会执行以下操作: 创建一个动态链接库——作为QML模块的插件

    2024年02月02日
    浏览(31)
  • HOJ 系统常用功能介绍 部署快速入门 c++ python java编程语言在线自动评测 信息奥赛一本通 USACO GESP 洛谷 蓝桥 CSP NOIP题库

    技术支持微  makytony   终身更新维护 功能类似洛谷和信息奥赛一本通,支持CSP复赛中的freopen文件输入输出方式提交,模拟真实考试环境,防止出现 本地  AC 比赛  WA  PA TLE  爆零 的惨剧。 组织比赛作业,创建题目、查看用户提交代码、下载评测数据等都没限制。 约  328

    2024年02月13日
    浏览(28)
  • By not providing “Findncnn.cmake“ in CMAKE_MODULE_PATH this project has asked CMake to find

    as报错: By not providing “Findncnn.cmake” in CMAKE_MODULE_PATH this project has asked CMake to find a package configuration file provided by “ncnn”, but CMake did not find one. 首先要下载导入,文件名称要与cmake文件对应正确 如果还是报这个错误 as问题,只需要删除 grade同步一下,然后再撤销删除grade同步就

    2024年02月11日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包